$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]);
}

猜你喜欢

转载自www.cnblogs.com/wo-shi-zhen-de-cai/p/11593104.html
今日推荐