联考20200726 T1 钩子



分析:
我们发现每个人操作时,不管前面的人怎么挂,当前这个人的\(d\)为定值
那么我们可以尝试以某种特定的方案模拟整个过程,再通过一些变换求出概率
假设目前的最大距离为\(d\),可以挂的空位置有\(m\)段,说明从这个人开始的后\(m\)个人都是这个\(d\),他们的概率可以一起算
\(m\)段的长度有奇数和偶数,奇数时可以直接挂在中点,偶数时中间两个点都可以挂
假设p段奇数,q段偶数,我们可以列出DP式\(f_{i,j,k}\)表示第\(i\)个人要挂奇数段时,奇数段剩\(j\)个,偶数段剩\(k\)个的概率
由于\(i+j+k=m\)\(i\)可以省去
用一个奇数段的概率为\(\frac{j}{j+2k}\),偶数段为\(\frac{2k}{j+2k}\)
表示第\(i\)个人要挂偶数段时,设一个DP式\(g_{i,j}\)同样处理
复杂度为\(O(n^2)\)
但是一个人选择挂偶数段时,两个点各自会产生新的情况,但是我们发现在这一段当中,挂左边和挂右边产生的情况是一一对称的
我们强行确定挂左边,并在中间记录一个对称轴,全部处理完之后,对称的位置取一次平均数就好了
特别注意,当最后\(d=1\)时,剩下的位置都可以选并且互不影响,不要进行上面的DP,直接暴力处理并结束就好了
复杂度式子冗杂,但整体上是不超过\(O(n^2)\)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<map>
#include<string>

#define maxn 1005

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,MOD;
int P[maxn],cur;
int vis[maxn];
int T[maxn*maxn],L[maxn*maxn],M[maxn*maxn],R[maxn*maxn],cnt;
int ans[maxn][maxn];
int f[maxn][maxn],g[maxn][maxn];
int inv[maxn];

inline int ksm(int num,int k)
{
	int ret=1;
	for(;k;k>>=1,num=1ll*num*num%MOD)if(k&1)ret=1ll*ret*num%MOD;
	return ret;
}

int main()
{
	n=getint(),MOD=getint();inv[0]=1;
	for(int i=1;i<=n;i++)inv[i]=ksm(i,MOD-2);
	for(int t=1;t<=n;t++)
	{
		P[cur=0]=0;
		for(int i=1;i<=n;i++)if(vis[i])P[++cur]=i;
		P[++cur]=n+1;
		int mx=0,tim=0,tot[2]={0};
		for(int i=cur;i;i--)P[i]=P[i]-P[i-1];
		for(int i=1;i<=cur;i++)
			if(P[i]/2>mx)mx=P[i]/2,tim=1+(P[i]&1),tot[0]=tot[1]=0,tot[P[i]&1]++;
			else if(P[i]/2==mx)tim+=1+(P[i]&1),tot[P[i]&1]++;
		int p=ksm(tim,MOD-2);
		if(mx==1)
		{
			for(int i=1;i<=n;i++)if(!vis[i])ans[t][i]=p;
			continue;
		}
		for(int i=0;i<=tot[0];i++)for(int j=0;j<=tot[1];j++)f[i][j]=g[i][j]=0;
		f[0][0]=g[0][0]=1;
		for(int tt=0;tt<tot[0]+tot[1];tt++)
		{
			int sumf=0,sumg=0;
			for(int i=max(0,tt-tot[1]);i<=min(tt,tot[0]);i++)
			{
				int j=tt-i,num=tot[0]-i+2*(tot[1]-j);
				sumf=(sumf+1ll*f[i][j]*inv[num])%MOD;
				sumg=(sumg+1ll*g[i][j]*inv[num])%MOD;
				if(i<tot[0])
					f[i+1][j]=(f[i+1][j]+1ll*f[i][j]*(tot[0]-i-1)%MOD*inv[num])%MOD,
					g[i+1][j]=(g[i+1][j]+1ll*g[i][j]*(tot[0]-i)%MOD*inv[num])%MOD;
				if(j<tot[1])
					f[i][j+1]=(f[i][j+1]+1ll*f[i][j]*2*(tot[1]-j)%MOD*inv[num])%MOD,
					g[i][j+1]=(g[i][j+1]+1ll*g[i][j]*2*(tot[1]-j-1)%MOD*inv[num])%MOD;
			}
			int now=0;
			for(int i=1;i<=cur;now+=P[i++])if(P[i]/2==mx)
			{
				if(P[i]&1)
				{
					ans[t+tt][now+mx]=ans[t+tt][now+mx+1]=sumg;
					T[++cnt]=t+1,L[cnt]=now+1,M[cnt]=now+mx,R[cnt]=now+P[i]-1;
				}
				else ans[t+tt][now+mx]=sumf;
				vis[now+mx]=1;
			}
		}
		t+=tot[0]+tot[1]-1;
	}
	for(int i=cnt;i;i--)
	{
		for(int j=T[i];j<=n;j++)for(int k=L[i];k<=M[i];k++)
			ans[j][k]=ans[j][R[i]+L[i]-k]=1ll*(ans[j][k]+ans[j][R[i]+L[i]-k])*inv[2]%MOD;
	}
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)printf("%d%c",ans[i][j],j==n?'\n':' ');
}

posted @ 2020-07-27 15:41  Izayoi_Doyo  阅读(181)  评论(0编辑  收藏  举报