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++

  • 相关阅读:
    Something I know about WebDynpro
    Details about support package implementation
    CRM Middleware Performance Topics
    Way to configure the logon navigaion layouts via Business Roles in CRM
    DOM 常用节点类型和方法
    第一届 xdef 会议日程
    去除百度音乐盒广告的chrome插件 持续更新
    从人人网抓取高校数据信息,包括,省份 高校 院系 (提供最终SQL文件下载)
    PHP 与 JSON
    解决HTTPS 发送请求走socket问题
  • 原文地址:https://www.cnblogs.com/wzc521/p/11624841.html
Copyright © 2011-2022 走看看