zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:Seat(概率DP+数学)

    题目描述

    有$n+2$个座位等距地排成一排,从左到右编号为$0$至$n+1$。
    最开始时$0$号以及$n+1$号座位上已经坐了一个小$G$,接下来会有$n$个小$G$依次找一个空座位坐下。由于小$G$们坐得太近就容易互相搏弈,每个小$G$会找一个当前离最近的小$G$距离最远的座位坐下。如果有多个备选的座位,这个小$G$会等概率选择其中一个。
    给出$n$,求第$i$个坐下的小$G$坐在$j$号座位的概率,对$P$取模。具体来说,如果答案化为最简分数可以表示为$frac{a}{b}$,你需要输出$a imes b^{−1}$,其中$b^{−1}=b^{P-2}(mod P)$。


    输入格式

    从文件$seat.in$中读入数据。
    一行两个整数$n,P$。


    输出格式

    输出到文件$seat.out$中。
    输出$n$个整数,第$i$行第$j$个整数表示第$i$个小$G$坐在第$j$个座位的概率。


    样例

    样例输入:

    4 10007

    样例输出:

    0 5004 5004 0
    3336 1668 1668 3336
    3336 1668 1668 3336
    3336 1668 1668 3336


    数据范围与提示

    样例解释:

    第一个小$G$会在中间两个位置中随机选择一个,接下来无论选哪个位置最近的距离都是$1$。
    $frac{1}{2}=5004(mod 10^4+7),frac{1}{3}=3336(mod 10^4+7),frac{1}{6}=1668(mod 10^4+7)$。

    数据范围:

    对于所有数据,满足$2leqslant nleqslant 1024,2000leqslant Pleqslant 30000$,$P$是质数。
    本题共$25$个测试点,每个测试点$4$分。


    题解

    好玄学的一道玄学题……

        一个结论是,对于任意一个人,他坐下时离最近的人的距离是固定的,不随前面的人的决策而改变。这样我们可以将所有人按坐下时的距离分成若干层。另一个结论是,无论之前每一层如何决策,轮到这一层时逬空区间的长度构成的可重集也是固定的。
        对于最后一层特殊处理接下来均默认不是最后一层。对于之前的每一层,考虑在哪些空区间中央坐下可使得距离最大,其中可能会有一些长度为奇数的区间和一些长度为偶数的区间,而对于每个人来说,坐在任意一个奇数的区间的中央的概率都是相等的,偶数同理。
        那么我们只需要知道,每个人有多大的概率坐在一个奇逯偶数区间的中央。考虑$DP$,$dp[i][j]$表示这一层已经坐下$i$个人之后,还有$j$个长度为偶数的区间的概率,转移只需考虑当前这个人坐了哪类区间即可。遤遰之后容易算出之前要求的概率。
        区间长度为奇数时位置是固定的;考虑区间长度为偶数的情况,此时会出现两个位置可供选择,但无论选择哪一个,都会将区间划分成长度为$frac{n}{2}$和$frac{n}{2}−1$的两段。因此这两种选择具有对称性,我们只需要假定选择其中的一个,算出这种情况下之后的层的答案,即可对称地推出另一种情况下的答案。
        瓶颈在利用对称性推答案的地方,主要看代码实现了,反正我是颓的代码。

    时间复杂度:$Theta(n^2log n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int n,P,now;
    int dp[1100][1100],ans[1100][1100],g[1100][1100],inv[1100],vis[1100],cnt[1100],odd[1100],pos[1100];
    int qpow(int x,int y)
    {
    	int res=1;
    	while(y)
    	{
    		if(y&1)res=res*x%P;
    		x=x*x%P;
    		y>>=1;
    	}
    	return res;
    }
    void pre_work()
    {
    	for(int i=1;i<=n;i++)inv[i]=qpow(i,P-2);
    	vis[0]=vis[n+1]=1;now=n;
    }
    int main()
    {
    	scanf("%d%d",&n,&P);
    	pre_work();
    	for(int i=1;i<=n;i++)
    	{
    		pair<int,int> lr=make_pair(0,0);
    		for(int j=0;j<=n;j++)
    		{
    			int flag=j+1;
    			while(!vis[flag])flag++;
    			if(flag-j>lr.second-lr.first){lr=make_pair(j,flag);}
    			j=flag-1;
    		}
    		 cnt[lr.second-lr.first>>1]++;
    		if((lr.second-lr.first)&1)odd[lr.second-lr.first>>1]++;
    		pos[i]=lr.first+(lr.second-lr.first>>1);
    		vis[lr.first+(lr.second-lr.first>>1)]++;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		if(!cnt[i])continue;
    		if(i==1)
    		{
    			for(int j=now-cnt[i]+1;j<=now;j++)
    				for(int k=now-cnt[i]+1;k<=now;k++)
    					ans[j][pos[k]]=inv[cnt[i]];
    		}
    		else
    		{
    			for(int j=0;j<=cnt[i];j++)
    				for(int k=0;k<=odd[i];k++)
    					dp[j][k]=0;
    			dp[0][odd[i]]=1;
    			for(int j=1;j<=cnt[i];j++)
    			{
    				int oddw=0,even=0;
    				for(int k=0;k<=odd[i];k++)
    				{
    					if(!dp[j-1][k])continue;
    					int frac=(cnt[i]-(j-1))+k;
    					int w=0;
    					if(k)
    					{
    						w=dp[j-1][k]*k*2%P*inv[frac]%P;
    						even=(even+w*inv[odd[i]<<1])%P;
    						dp[j][k-1]=(dp[j][k-1]+w)%P;
    					}
    					if(cnt[i]-odd[i])
    					{
    						w=dp[j-1][k]*(frac-2*k)%P*inv[frac]%P;
    						oddw=(oddw+w*inv[cnt[i]-odd[i]])%P;
    						dp[j][k]=(dp[j][k]+w)%P;
    					}
    				}
    				for(int k=now-cnt[i]+1;k<=now-cnt[i]+odd[i];k++)
    				{
    					ans[now-cnt[i]+j][pos[k]]=(ans[now-cnt[i]+j][pos[k]]+even)%P;
    					ans[now-cnt[i]+j][pos[k]+1]=(ans[now-cnt[i]+j][pos[k]+1]+even)%P;
    				}
    				for(int k=now-cnt[i]+odd[i]+1;k<=now;k++)
    					ans[now-cnt[i]+j][pos[k]]=(ans[now-cnt[i]+j][pos[k]]+oddw)%P;
    			}
    			for(int j=now-cnt[i]+1;j<=now-cnt[i]+odd[i];j++)
    			{
    				int L=pos[j]-i+1;
    				int R=pos[j]+i;
    				for(int k=L;k<=R;k++)
    				{
    					if(k==pos[j])continue;
    					for(int l=now+1;l<=n;l++)
    					{
    						g[l][k]=(g[l][k]+ans[l][k]*inv[2])%P;
    						g[l][k<pos[j]?k+i+1:k-i]=(g[l][k<pos[j]?k+i+1:k-i]+ans[l][k]*inv[2])%P;
    					}
    				}
    				for(int k=L;k<=R;k++)
    					for(int l=now+1;l<=n;l++)
    					{
    						ans[l][k]=g[l][k];
    						g[l][k]=0;
    					}
    			}
    		}
    		now-=cnt[i];
    	}
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=n;j++)
    			printf("%d ",ans[i][j]);
    		puts("");
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    Ubuntu18.04配置静态ip遇到的报错
    devilbox(二):连接数据库
    DBeaver
    prometheus-operator 详细总结(helm一键安装)
    如何创建私有 CA 并签发证书
    kong如何记录(nginx代理后)真实ip
    节点亲和性添加
    alertmanager详解
    subprocess.call和subprocess.Popen
    dockerfile-ENTRYPOINT 和CMD配合,以及他们的区别
  • 原文地址:https://www.cnblogs.com/wzc521/p/11624841.html
Copyright © 2011-2022 走看看