$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\{\begin{array}{ll}(i-1)!*i*(i+j)^{j-1}&i>0\\(i-1)!&i=0 \end{array}\right. \]

所以\(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]);
}

posted @ 2019-09-26 17:16  风骨傲天  阅读(228)  评论(0编辑  收藏  举报