首页 > 其他分享 >CF1919E

CF1919E

时间:2024-03-09 10:34:38浏览次数:24  
标签:int res rep CF1919E fac mx mod

@Explodingkonjac 学长讲的做法,题解区有一篇讲这个的,但是感觉细节真的好多……

我们先尝试构造出来一个合法序列。怎么构造呢?先枚举 \(\sum a_i=s\),然后先将序列 \(a\) 设为 \(\max(p_n,0)\) 个 \(1\) 后面接 \(\max(p_n,0)-s\) 个 \(-1\),也就是先到最大值再到最终值。

然后考虑往这个数列中插入若干 \(\{-1,1\}\) 这个序列。画到一个折线图上,发现这就相当于增加一块凹陷。不难发现,对于一个有解的 \(s\),这样一定能构造出解。

设新前缀和序列为 \(p'\) 同时,在一个 \(p_i'=k\) 的位置后面插入一个 \(\{-1,1\}\) 能使 \(k,k-1\) 在 \(p'\) 的出现次数增加 \(1\)。于是在构造的同时就可以计数了!

具体地,从大往小考虑 \(x\) 的出现次数。设 \(x\) 在 \(p\) 中出现 \(a_x\) 次,\(p'\) 中 \(b_x\) 次,则要通过插入 \(\{-1,1\}\) 的操作将 \(b_X\) 加到 \(a\)。反过来,一共要将 \(a_x\) 个 \(x\) 分到 \(b_x\) 个集合中,插板法,贡献为 \(\binom{a-1}{b-1}\)。然后 \(b_{x-1}\) 会加上 \(a_x-b_x\),继续往下推即可。

细节包括但不限于:

  • \(p'\) 要考虑一开始的 \(0\) 否则一开始下降的情况统计不到。

  • \(a_x=0\) 的情况要注意一下。

  • 等等。

code:

点击查看代码
int n,m,fac[N],ifac[N],c[N],b[N];
il int Mod(int x,int y){return x+y>=mod?x+y-mod:x+y;}
il int binom(int x,int y){
	if(x<0||y<0||x<y)return 0;
	return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
il int qpow(int x,int y){
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		x=1ll*x*x%mod,y>>=1;
	}
	return ret;
}
void Yorushika(){
	scanf("%d",&n);
	int mx=0;
	rep(i,0,n+n)c[i]=0;
	c[n]=1;
	rep(i,1,n){
		int x;scanf("%d",&x);
		c[x+n]++,mx=max(mx,x+n);
	}
	fac[0]=1;
	rep(i,1,n)fac[i]=1ll*fac[i-1]*i%mod;
	ifac[n]=qpow(fac[n],mod-2);
	drep(i,n-1,0)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
	int ans=0;
	rep(j,0,n+n){
		if(!c[j])continue;
		int x=n,res=1;
		rep(i,0,n+n)b[i]=0;
		b[n]=1;
		rep(i,1,mx-n)b[++x]++;
		rep(i,1,max(mx,n)-j)b[--x]++;
		bool fl=1;
		if(!fl)continue;
		drep(i,n+n,0){
			if(!c[i]){if(b[i]){res=0;break;}continue;}
			res=1ll*res*binom(c[i]-1,b[i]-1)%mod;
			if(i)b[i-1]+=c[i]-b[i];
		}
		ans=Mod(ans,res);
	}
	printf("%d\n",ans);
}
signed main(){
	int t=1;
		scanf("%d",&t);
	while(t--)
		Yorushika();
}

标签:int,res,rep,CF1919E,fac,mx,mod
From: https://www.cnblogs.com/yinhee/p/18062348/CF1919E

相关文章