zoukankan      html  css  js  c++  java
  • $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]);
    }
    
    
  • 相关阅读:
    雷林鹏分享:EJB回调
    雷林鹏分享:EJB注解
    雷林鹏分享:EJB持久性
    雷林鹏分享:EJB有状态会话Bean
    雷林鹏分享:EJB无状态Bean
    雷林鹏分享:EJB创建应用
    雷林鹏分享:EJB概述
    雷林鹏分享:EJB教程
    Java集合框架
    Java集合源码剖析——ArrayList源码剖析
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11593104.html
Copyright © 2011-2022 走看看