1 条题解

  • 0
    @ 2026-5-13 8:35:09

    我太菜了,感觉这题评黄也行。

    题意简述

    nn 位玩家,每人有一张写有 kk 个数字的卡片。玩家按编号 1n1 \sim n 反应速度递减(编号越小反应越快)。

    游戏主持人以随机顺序喊出所有 n×kn \times k 个数字(包含重复)。每当喊出一个数字时,所有卡片上有该数字且尚未划完该数字的玩家中,反应最快的那位将其划掉一个。

    游戏一直进行到所有玩家划完所有数字。求每位玩家最后完成的概率。

    输出 nn 行,结果精确到 10610^{-6}

    思路

    考虑一个在所有卡片中共出现 mm 次的数字 xx

    游戏进行到某个时刻,当 xx最后一个副本被喊出时,拥有该数字且编号最大(反应最慢)的玩家会划掉它。

    如果这个副本恰好是全场最后一个被喊出的数字,那么划掉它的玩家就是全场最后完成的人。

    在完全随机的排列中,数字 xx 的某个副本成为“全场最后一个被喊出”的概率为 mn×k\frac{m}{n \times k}

    而无论 xx 的最后一个副本在什么时候出现,划掉它的人始终是拥有 xx 的最慢玩家。

    因此,我们可以把“最后完成”的责任分配到每个数字上:

    数字 xxmn×k\frac{m}{n \times k} 的概率成为全场最后一个被喊出的数字,而这个“拖到最后”的后果由拥有 xx 的最慢玩家承担。

    算法步骤

    1. 统计每个数字在所有卡片中的总出现次数。
    2. 倒序遍历玩家,对于当前玩家的每个数字,如果该数字还没有被更慢的玩家认领过,则将它的出现次数加到当前玩家头上,并标记该数字已被认领。这样做保证了:当多个玩家拥有同一数字时,只有编号最大的那个人获得该数字的权重。
    3. 输出每位玩家的累计次数除以总次数 n×kn \times k

    注意到:

    所有数字均在 1110910^9 之间(含边界)。

    所以开桶直接用 map 或者 unordered_map 即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    int a[105][1005],n,k,s[105];
    unordered_map<int,int>mp,u;
    int main(){
    	cin>>n>>k;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=k;j++) cin>>a[i][j],mp[a[i][j]]++;//统计出现次数
    	}
    	for(int i=n;i;i--){
    		for(int j=1;j<=k;j++){
    			if(!u[a[i][j]]) s[i]+=mp[a[i][j]],u[a[i][j]]=1;//累计每个人分配到的数字的出现个数
    		}
    	}
    	for(int i=1;i<=n;i++) printf("%.6lf\n",s[i]*1.0/(n*k));//计算概率
    }
    
    • 1

    「ICPC World Finals 2024」宾果游戏的胜利!

    信息

    ID
    8567
    时间
    1000ms
    内存
    2048MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者