$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
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

你可能感兴趣的:($Luogu[P3673]$小清新计数题)