$Luogu[P3673]$小清新计数题

这他妈什么玩意儿……

这里是可爱的链接菌

转化模型,对于第(i)句话:“第(p)句话为真话”,将(i)(p)连一条白边,“第(p)句话为假话”,将(i)(p)连一条黑边。

显然我们的图会是一片基环树森林,并且边为无向边,白边连的两点真假相同,黑边相反。

那么要使森林符合要求,每个基环树的环上必定有偶数个黑边。(证明口胡如下)

我们考虑树(没有环),对于一棵树无论如何一定是满足条件的,(这显然)所以重点在环上。

对于黑边,我们可以看做将一个环分为了几段,每一段中点真假性相同,相邻的段真假性不同。

所以要在环上有偶数个黑边。

考虑一颗有(i)条白边,(j)条黑边的基环树方案数。

先枚举环上的黑白边条数:(a)条白边,(b)条黑边,设(n=a+b,m=i+j-a-b)

每个点都有编号,所以环上的方案数为((n-1)!),而环上加点的方案数为(n*(n+m)^{m-1})。(实际我也不会证,当结论记着吧……)

我们预处理一个(g[i][j])

[g[i][j]=left{egin{array}{ll}(i-1)!*i*(i+j)^{j-1}&i>0\(i-1)!&i=0 end{array} ight. ]

所以(f[i][j])就有:

[f[i][j]=sum_{a+b<=i+j,!b&1}{i choose a}{j choose b}g[a+b][i+j-a-b] ]

最后统计答案时需要钦定(1)的位置,不然会计算重复。

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=100;
const int p=998244353;
int Blk,Wht,n;
int f[N][N],g[N][N],C[N][N],ans[N][N];
inline int ksm(int b,int k)
{
	int a=b;if(!k) return 1;k--;
	while(k)
	{
		if(k&1) a=(a%p*b%p)%p;
		b=(b%p*b%p)%p;
		k>>=1;
	}
	return a%p;
}
main(){
#ifndef ONLINE_JUDGE
    //freopen("A.in","r",stdin);//Ans=1
	//freopen("B.in","r",stdin);//Ans=1154
	freopen("C.in","r",stdin);//Ans=322173207
#endif
	string s;cin>>s;n=s.size();
	for(int i=0;i<n;i++) Wht+=s[i]-'0';Blk=n-Wht;
	for(int i=0;i<=n;i++) C[i][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			C[i][j]=(C[i-1][j]%p+C[i-1][j-1]%p+p)%p;
	g[1][0]=1;for(int i=2;i<=n;i++) g[i][0]=(g[i-1][0]%p*(i-1)%p+p)%p;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n-i;j++)
			g[i][j]=(g[i][0]%p*i%p*ksm(i+j,j-1)%p+p)%p;
	for(int i=0;i<=Wht;i++)
		for(int j=0;j<=Blk;j++)
			for(int a=0;a<=i;a++)
				for(int b=0;b<=j;b+=2)
					if((a|b)&&a+b<=i+j)
						f[i][j]=(f[i][j]%p+(C[i][a]%p*C[j][b]%p*g[a+b][i+j-a-b]%p)%p+p)%p;
	ans[0][0]=1;
	for(int i=0;i<=Wht;i++)
		for(int j=0;j<=Blk;j++)
			if(i|j)
			{
				if(i) for(int x=1;x<=i;x++)
						  for(int y=0;y<=j;y++)
							  ans[i][j]=(ans[i][j]%p+ans[i-x][j-y]%p*C[i-1][x-1]%p*C[j][y]%p*f[x][y]%p+p)%p;
				else for(int y=1;y<=j;y++)
						 ans[i][j]=(ans[i][j]%p+ans[i][j-y]%p*C[j-1][y-1]%p*f[i][y]%p+p)%p;
			}
	printf("%lld",ans[Wht][Blk]);
}

原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11593104.html