zoukankan      html  css  js  c++  java
  • 2018雅礼集训 方阵 和 BZOJ4671 异或图

    方阵

    给定 (n × m) 的矩阵,每个格子填上 ([1, c]) 中的数字,求任意两行、两列均不同的方案数。

    (n, m ≤ 5000)

    题解

    我们默认矩阵为 (n) 行的,以下将 (n) 视为常数。

    (g(m)) 表示 (m) 列的矩阵,满足任意两行不相同的方案数。那么

    [g(m)=(c^m)^underline{n} ]

    (f_m(i)) 表示 (m) 列的矩阵,满足任意两行不相同,且列总共分为 (i) 类的方案数。那么

    [g(m)=sum_{i=0}^megin{Bmatrix}m\iend{Bmatrix}f_m(i)\ f_m(m)=sum_{i=0}^m(-1)^{m-i}egin{bmatrix}m\iend{bmatrix}g(i)\ =sum_{i=0}^m(-1)^{m-i}egin{bmatrix}m\iend{bmatrix}(c^i)^underline{n} ]

    时间复杂度 (O(nm))

    CO int N=5000+10;
    int S[N][N],P[N];
    
    void real_main(){
    	int n=read<int>(),m=read<int>(),c=read<int>();
    	P[0]=1;
    	for(int i=1;i<=m;++i) P[i]=mul(P[i-1],c);
    	int ans=0;
    	for(int i=1;i<=m;++i){
    		int sum=S[m][i];
    		for(int j=0;j<n;++j) cmul(sum,P[i]-j);
    		cadd(ans,(m-i)&1?mod-sum:sum);
    	}
    	printf("%d
    ",ans);
    }
    int main(){
    	S[0][0]=1;
    	for(int i=1;i<N;++i)for(int j=1;j<=i;++j)
    		S[i][j]=add(S[i-1][j-1],mul(i-1,S[i-1][j]));
    	for(int T=read<int>();T--;) real_main();
    	return 0;
    }
    

    BZOJ4671 异或图

    定义两个结点数相同的图 (G_1) 与图 (G_2) 的异或为一个新的图 (G), 其中如果 ((u, v))(G_1)(G_2) 中的出现次数之和为 (1), 那么边 ((u, v))(G) 中, 否则这条边不在 (G) 中.

    现在给定 (s) 个结点数相同的图 (G_{1dots s}), 设 (S = {G_1, G_2, dots , G_s}), 请问 (S) 有多少个子集的异或为一个连通图?

    (2 ≤ n ≤ 10,1 ≤ s ≤ 60.)

    题解

    连通性计数相关的问题一般要用到容斥原理,这是因为“连通”非常难处理,因为整体连通并不知道每条边的存在情况,而“不连通”则是可以确定没有任何边相连;而容斥就是用 连通方案=总方案?不连通方案连通方案=总方案?不连通方案,从而将连通计数问题转化为不连通计数的问题。

    (f_i)表示至少有(i)个联通块的方案,形如设立(i)个联通块轮廓,联通块内连边随意,联通块与联通块之间无连边。

    (g_i)表示恰好有(i)个联通块的方案,形如设立(i)个联通块轮廓,在保证内部联通的情况下,外部块与块间无连边。

    显然:

    [f_x=sumlimits_{i=x}^negin{Bmatrix}i\xend{Bmatrix}g_i ]

    根据斯特林反演:

    [g_x=sumlimits_{i=x}^n (-1)^{i-x}egin{bmatrix}i\xend{bmatrix}f_i ]

    [g_1=sumlimits_{i=1}^n (-1)^{i-1}egin{bmatrix}i\1end{bmatrix}f_i ]

    (egin{bmatrix}i\1end{bmatrix})是阶乘形式(egin{bmatrix}i\1end{bmatrix}=(i-1)!),化简答案为

    [g_1=sumlimits_{i=1}^n (-1)^{i-1}(i-1)!f_i ]

    考虑如何求出(f_i)

    我们用( ext{Bell}(n))的复杂度枚举子集划分,把每个子集作为一个块,两个不同块之间不能连边,块内任意。那么用(x_1,x_2,dots,x_s)表示图的选择情况,可以得出对每条跨块的边的选择情况的异或方程组

    [x_1[ein G_1] ext{xor} x_2[ein G_2] ext{xor} dots ext{xor} x_s[ein G_s]=0 ]

    对这个方程组进行高斯消元得到秩(c),那么自由元的数量就是(s-c)。所以(ans=2^{s-c})

    需要将跨越块的边单独拿出来重新编号,可以利用线性基来实现快速消元。

    co int maxs=65,maxn=15,maxe=50;
    char str[maxe];
    int s,n,gr[maxs][maxn][maxn];
    
    int ans,A[maxe],fac[maxn];
    int bl[maxn];
    void solve(int cur,int m){
    	if(cur>=n){
    		memset(A,0,sizeof A);
    		for(int i=0;i<n;++i)
    			for(int j=i+1;j<n;++j)if(bl[i]!=bl[j]){
    				int tmp=0;
    				for(int g=0;g<s;++g) tmp|=gr[g][i][j]<<g;
    				for(int k=maxe-1;k>=0;--k)if(tmp>>k&1){
    					if(A[k]) tmp^=A[k];
    					else {A[k]=tmp;break;}
    				}
    			}
    		int c=0;
    		for(int k=0;k<maxe;++k) c+=A[k]>0;
    		ans+=(m&1?1:-1)*(1LL<<(s-c))*fac[m-1];
    		return;
    	}
    	for(int i=1;i<=m+1;++i)
    		bl[cur]=i,solve(cur+1,max(m,i));
    }
    
    signed main(){
    	read(s);
    	for(int i=0;i<s;++i){
    		scanf("%s",str);
    		int l=strlen(str),t=0;
    		n=(1+sqrt(1+(l<<3)))/2;
    		for(int x=0;x<n;++x)
    			for(int y=x+1;y<n;++y)
    				gr[i][x][y]=str[t++]-'0';
    	}
    	fac[0]=1;
    	for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i;
    	solve(0,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java设计模式之单例模式
    Bootstrap-table使用footerFormatter做统计列
    Bootstrap进度条
    基于Bootstrap的表格插件bootstrap-table
    基于Bootstrap的对话框插件bootstrap-dialog
    基于Bootstrap的下拉框插件bootstrap-select
    JsonConfig处理日期时间
    Jquery表单验证插件validate
    Hibernate使用Criteria去重distinct+分页
    设置iframe高度自适应屏幕高度
  • 原文地址:https://www.cnblogs.com/autoint/p/12149566.html
Copyright © 2011-2022 走看看