ABC321G

发布时间 2023-12-23 21:45:46作者: yinhee

其实赛时可能可以做出来的,只是打了前 6 道想下班了,有点小小遗憾。

首先问题看起来很唬人,考虑转换一下。考虑已经固定 \(m\) 条边,对于一个集合 \(S\),什么时候会不与其他点有边。容易发现,此时需要满足 \(\sum[R_i\in S]=\sum [B_j\in S]\)。记这个数为 \(c_S\)。但是这还不够,因为 \(S\) 中点连边方案数还没有计算。

这个答案显然不是 \(c_S!\)。因为 \(S\) 可能可以分成两个集合 \(s,t\) 都满足上述条件,则会重复计算。考虑如何去重。设 \(dp_s\) 为集合 \(S\) 的答案。我们钦定 \(t\) 包含 \(S\) 里最小的元素,则有 \(dp_s=c_s!-\sum_{t\in S} dp_t\times (c_s-c_t)!\)

然后怎么办呢?

作者表示再做一遍 dp,\(O(m3^m)\) 解决。还真过了。

但是事实上 \(ans=\sum dp_s\times (m-c_s)!\)。解决。

code:

点击查看代码
int n,m,e[23],d[23],f[N],g[N],h[N],c[N];
il int Mod(int x,int y){return x+y>=mod?x+y-mod:x+y;}
il int lowbit(int x){return x&(-x);}
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%d",&n,&m);
	f[0]=1;
	rep(i,1,m){
		int x;scanf("%d",&x);
		e[x]++;
		f[i]=1ll*f[i-1]*i%mod;
	}
	rep(i,1,m){
		int x;scanf("%d",&x);
		d[x]++;
	}
	const int k=1<<n;
	int ans=0;
	rep(i,1,k-1){
		rep(j,0,n-1){
			g[i]+=e[j+1]*(i>>j&1);
			h[i]+=d[j+1]*(i>>j&1);
		}
		if(g[i]!=h[i])
			continue;
		c[i]=f[g[i]];
		for(int j=(i-1)&i;j;j=(j-1)&i){
			if(g[j]!=h[j]||lowbit(i)!=lowbit(j))
				continue;
			c[i]=Mod(c[i],mod-1ll*c[j]*f[g[i]-g[j]]%mod);
		}
		ans=Mod(ans,1ll*c[i]*f[m-g[i]]%mod);
	}
	printf("%lld\n",1ll*ans*qpow(f[m],mod-2)%mod);
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}