• JZOJ6442. 【GDOI2020模拟01.18】钩子


    Description

    传送门

    在这里插入图片描述
    n<=1000n<=1000

    Solution

    • 我们可以模拟这个过程。
    • 一种简单的方法是对于奇数长度段直接分,对于偶数长度的段有两个位置可以选,我们可以钦定它选前一个位置,然后再继续往下分,最后再以这个位置在这个段内将所有的在这之后的人的概率对称过去。
    • 具体来说,我们可以发现对于所有的当前的段,(len1)/2(len-1)/2最大且相同的一些段要一起做,考虑一共有mm个,那么接下来的mm个人都填这些段。
    • 但是我们注意到,由于偶数位置有两个,而且这两个在所有的位置中都要占概率,所以要用DP来计算,钦定一个位置(奇/偶)不选,剩下的位置选了i个奇,j个偶的概率是f[i][j]/g[i][j]f[i][j]/g[i][j],然后就可以计算出这mm个中的第i+ji+j个在某个奇或某个偶的位置的贡献了。
    • 最后对称过去就好了。
    • 由于这个分治是类似线段树的,所以一共有loglog层,每一层最坏是n2n^2的(实际上远远到不了),最后的对称也是n2lognn^2logn的,因为只有在某个操作之后的人才要被这个操作对称。
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    #define maxn 1005
    using namespace std;
    
    int n,mo,c[maxn],a[maxn][maxn][2],i,j,k,l,now,p[maxn],q[maxn],nowc,nc[maxn];
    ll inv[maxn],ans[maxn][maxn],f[maxn][maxn],g[maxn][maxn];
    
    ll ksm(ll x,ll y){
    	ll s=1;
    	for(;y;y/=2,x=x*x%mo) if (y&1)
    		s=s*x%mo;
    	return s;
    }
    
    int main(){
    	scanf("%d%d",&n,&mo);
    	for(i=1;i<=n;i++) inv[i]=ksm(i,mo-2);
    	c[0]=1,a[0][1][0]=1,a[0][1][1]=n,now=nowc=0;
    	while (1){
    		int mx=0;
    		for(i=1;i<=c[now];i++) mx=max(mx,(a[now][i][1]-a[now][i][0])/2);
    		if (mx==0){
    			int cnt=0;
    			for(i=1;i<=c[now];i++) 
    				cnt+=a[now][i][1]-a[now][i][0]+1;
    			for(i=1;i<=c[now];i++)
    				for(j=a[now][i][0];j<=a[now][i][1];j++)
    					for(k=1;k<=cnt;k++)
    						ans[nowc+k][j]+=inv[cnt];
    			break;
    		}		
    		p[0]=q[0]=0;
    		for(i=1;i<=c[now];i++) if ((a[now][i][1]-a[now][i][0])/2==mx){
    			k=(a[now][i][1]-a[now][i][0])/2;
    			if ((a[now][i][1]-a[now][i][0]+1)&1)
    				p[++p[0]]=a[now][i][0]+k;
    			else q[++q[0]]=a[now][i][0]+k;
    			c[now+1]++;
    			a[now+1][c[now+1]][0]=a[now][i][0];
    			a[now+1][c[now+1]][1]=a[now][i][0]+k-1;
    			c[now+1]++;
    			a[now+1][c[now+1]][0]=a[now][i][0]+k+1;
    			a[now+1][c[now+1]][1]=a[now][i][1];
    		} else {
    			c[now+1]++;
    			a[now+1][c[now+1]][0]=a[now][i][0];
    			a[now+1][c[now+1]][1]=a[now][i][1];
    		}
    		for(i=0;i<=p[0];i++) for(j=0;j<=q[0];j++) f[i][j]=g[i][j]=0;
    		f[0][0]=g[0][0]=1;
    		for(k=1;k<=p[0]+q[0];k++){
    			ll sum0=0,sum1=0;
    			for(i=max(k-1-q[0],0);i<=min(p[0],k-1);i++){ j=k-1-i;
    				int res=p[0]-i+2*(q[0]-j);
    				if (i<p[0]) sum0+=f[i][j]*inv[res]%mo;
    				if (j<q[0]) sum1+=g[i][j]*2*inv[res]%mo;
    			}
    			sum0%=mo,sum1%=mo;
    			for(i=1;i<=p[0];i++) ans[nowc+k][p[i]]+=sum0;
    			for(i=1;i<=q[0];i++) ans[nowc+k][q[i]]+=sum1;
    			if (k==p[0]+q[0]) break;
    			for(i=max(k-1-q[0],0);i<=min(p[0],k-1);i++){j=k-1-i;
    				int res=p[0]-i+2*(q[0]-j);
    				if (i<p[0]) {
    					f[i+1][j]+=f[i][j]*(p[0]-i-1)%mo*inv[res]%mo;
    					g[i+1][j]+=g[i][j]*(p[0]-i)%mo*inv[res]%mo;
    				}
    				if (j<q[0]) {
    					f[i][j+1]+=f[i][j]*2*(q[0]-j)%mo*inv[res]%mo;
    					g[i][j+1]+=g[i][j]*2*(q[0]-j-1)%mo*inv[res]%mo;
    				}
    			}
    		}
    		nowc+=p[0]+q[0],nc[++now]=nowc;
    	}
    	for(now--;now>=0;now--){
    		for(i=1;i<=c[now];i++) if (!((a[now][i][1]-a[now][i][0]+1)&1)){
    			int mid=(a[now][i][0]+a[now][i][1])/2;
    			for(j=a[now][i][0];j<=mid;j++){
    				l=a[now][i][1]-(j-a[now][i][0]);
    				for(k=nc[now]+1;k<=n;k++){
    					ll s=(ans[k][j]+ans[k][l])*inv[2]%mo;
    					ans[k][j]=ans[k][l]=s;
    				}
    			}
    		}
    	}
    	for(i=1;i<=n;i++,printf("
    "))
    		for(j=1;j<=n;j++)
    			printf("%lld ",ans[i][j]);
    }
    
  • 相关阅读:
    C#面向对象之封装。
    python 数据处理学习pandas之DataFrame
    有用的vscode快捷键大全+自定义快捷键
    angular中控制器之间传递参数的方式
    angular.module 详解
    如何让类数组也使用数组的方法比如:forEach()
    CSS之flex兼容
    JavaScript中捕获/阻止捕获、冒泡/阻止冒泡
    Vue2.0 探索之路——生命周期和钩子函数的一些理解
    React 生命周期
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090888.html
Copyright © 2020-2023  润新知