CF1799G

同样来自 @Explodingkonjac 学长的讲题。但是我没认真听讲,所以自己想出来了。

原本的想法是设对于每一组分别设 dpi,j 为当前枚举到第 i 个位置,已经钦定了 j 个该组中的人投给自己组的方案数。转移就是枚举有多少人投给 i 然后容斥。

但是可能是我没有处理好,总之这种转移似乎使每次切换组时就算上组内没被钦定的数的贡献。然而这样是错的因为后面组剩余的数量就是不确定的,有后效性了。

此时再仔细分析代码,发现复杂度不会超过 i(j[tj=i])2n2。也就是说可以再加一维。

那么变成设 dpi,j,k 为当前枚举到第 i 个位置,已经钦定了 j 个人投给同组的人,已经钦定了 k 个该组中的人投给自己组的方案数。转移是类似的,这样就可以最后再算没钦定的人的贡献了。时间复杂度 O(n3) 可以通过。

但是做完看题解发现原来有 O(n2) 做法,吐槽数据范围。大概做法就是把同组的一起算,再乘上组内排列贡献。

code(O(n3)):

code:

点击查看代码
int n,m,dp[N][N][N],c[N],fac[N],ifac[N];
struct node{int x,y;}a[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 init(int 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;
}
il bool cmp(node x,node y){return x.y<y.y;}
void Yorushika(){
	scanf("%d",&n);
	rep(i,1,n)scanf("%d",&a[i].x);
	rep(i,1,n){
		int x;scanf("%d",&x);
		c[x]++,a[i].y=x;
	}
	init(n);
	sort(a+1,a+n+1,cmp);
	dp[0][0][0]=1;
	rep(i,1,n){
		int s=c[a[i].y];
		rep(l,0,a[i].x){
			rep(j,l,n)rep(k,l,c[a[i].y]){
				if(l&1)dp[i][j][k]=Mod(dp[i][j][k],mod-1ll*dp[i-1][j-l][k-l]*binom(s-k+l,l)%mod*ifac[a[i].x-l]%mod);
				else dp[i][j][k]=Mod(dp[i][j][k],1ll*dp[i-1][j-l][k-l]*binom(s-k+l,l)%mod*ifac[a[i].x-l]%mod);
			}
		}
		if(a[i+1].y!=a[i].y){
			rep(j,0,n){
				rep(k,1,s)dp[i][j][0]=Mod(dp[i][j][0],dp[i][j][k]),dp[i][j][k]=0;
			}
		}
	}
	int ans=0;
	rep(i,0,n)ans=Mod(ans,1ll*dp[n][i][0]*fac[n-i]%mod);
	printf("%d\n",ans);
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}
posted @   yinhee  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示