zoukankan      html  css  js  c++  java
  • 【BZOJ4671】异或图(斯特林反演)

    【BZOJ4671】异或图(斯特林反演)

    题面

    BZOJ

    Description

    定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与
    G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中.
    现在给定 s 个结点数相同的图 G1...s, 设 S = {G1, G2, . . . , Gs}, 请问 S 有多少个子集的异
    或为一个连通图?

    Input

    第一行为一个整数s, 表图的个数.
    接下来每一个二进制串, 第 i 行的二进制串为 gi, 其中 gi 是原图通过以下伪代码转化得
    到的. 图的结点从 1 开始编号, 下面设结点数为 n.

    Algorithm 1 Print a graph G = (V, E)
    for i = 1 to n do
        for j = i + 1 to n do
            if G contains edge (i, j) then
                print 1
            else
                print 0
            end if
        end for
    end for
    

    $ 2 ≤ n ≤ 10,1 ≤ s ≤ 60.$

    Output

    输出一行一个整数, 表示方案数

    Sample Input
    3

    1

    1

    0
    Sample Output
    4

    题解

    连通很难处理,正难则反,考虑总方案减去不连通。
    总方案随便算,要考虑的只有不连通的方案数。
    如果不连通的话,我们考虑其子集的划分,子集之间必定不存在边连通。那么我们考虑其连通块个数,设(f_i)表示恰好有(i)个连通块的方案数,(g_i)表示至少有(i)个连通块的划分的计算的结果。
    考虑两者之间的关系:

    [g_k=sum_{i=k}^n egin{Bmatrix}i\kend{Bmatrix}f_i ]

    原因很简单,如果直接考虑枚举连通块数量考虑划分的话,因为一个随意的连通块可能由多个连通块组成,意味着会被反复计算多次。那么对于(k)个连通块而言,其被计算的次数就是(displaystyle egin{Bmatrix}i\kend{Bmatrix})。这个次数是考虑计算至少(m)的时候,多出来的连通块会被划分到其他连通块里面去,实际上等价于把当前实际存在的(i)个连通块划分为(k)个计算到至少的贡献里面去。
    根据斯特林反演,可以得到:

    [f_k=sum_{i=k}^n(-1)^{i-k}egin{bmatrix}i\kend{bmatrix}g_i ]

    我们要求的是恰好一个连通块的方案数,即(f_1)

    [egin{aligned} f_1&=sum_{i=1}^n(-1)^{i-1}egin{bmatrix}i\1end{bmatrix}g_i\ &=sum_{i=1}^n(-1)^{i-1}(i-1)!g_i end{aligned}]

    考虑如何计算(g)。现在要通过(g)来计算(f)。所以我们显然需要找到一个方法能够直接计算(g)
    回到题目给定的条件,发现点数很少而图的数量很多。枚举一个子集划分,因为所求是至少,所以只需要确定不同子集之间不存在边。把这些边抠出来,要求的就是满足这些位置异或起来为(0)的方案数,构建线性基,答案显然是(2^{s-c}),其中(c)是线性基内元素的个数。那么全部累加到(g)中去最后反演计算(f)即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define ll long long
    #define MAX 65
    #define MAXN 15
    int G[MAX][MAXN][MAXN];
    char ch[MAX];
    int n,K,a[MAXN];
    ll ans,p[MAX],jc[MAXN];
    void dfs(int x,int t)
    {
    	if(x==n+1)
    	{
    		memset(p,0,sizeof(p));int ele=0;
    		for(int k=1;k<=K;++k)
    		{
    			ll S=0;int tot=0;
    			for(int i=1;i<=n;++i)
    				for(int j=i+1;j<=n;++j)
    					if(a[i]!=a[j])S|=(1ll<<tot)*G[k][i][j],++tot;
    			for(int i=0;i<tot;++i)
    				if(S&(1ll<<i))
    				{
    					if(!p[i]){p[i]=S;++ele;break;}
    					else S^=p[i];
    				}
    		}
    		ans+=1ll*((t&1)?1:-1)*jc[t-1]*(1ll<<(K-ele));
    		return;
    	}
    	for(int i=1;i<=t+1;++i)
    		a[x]=i,dfs(x+1,max(i,t));
    }
    int main()
    {
    	scanf("%d",&K);
    	for(int i=1;i<=K;++i)
    	{
    		scanf("%s",ch+1);int l=strlen(ch+1),cnt=0;
    		for(int j=1;!n;++j)if(j*(j-1)==l+l)n=j;
    		for(int j=1;j<=n;++j)
    			for(int k=j+1;k<=n;++k)
    				G[i][j][k]=ch[++cnt]-48;
    	}
    	jc[0]=1;for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i;
    	dfs(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    HDU 4287 Intelligent IME 第37届ACM/ICPC天津赛区网络赛1010题 (水题)
    HDU 4267 A Simple Problem with Integers 第37届ACM/ICPC长春赛区网络赛1001题 (树状数组)
    HDU 4277 USACO ORZ 第37届ACM/ICPC长春赛区网络赛1011题(搜索)
    HDU 4099 Revenge of Fibonacci(字典树)
    HDU 2802 F(N)(简单题,找循环解)
    HDU 4282 A very hard mathematic problem 第37届ACM/ICPC长春赛区网络赛1005题 (暴力)
    HDU 4268 Alice and Bob 第37届ACM/ICPC长春赛区网络赛1002题 (贪心+multiset)
    HDU 3501 Calculation 2(欧拉函数的引申)
    HDU 4278 Faulty Odometer 第37届ACM/ICPC天津赛区网络赛1001题 (简单水题)
    HDU 4279 Number 第37届ACM/ICPC天津赛区网络赛1002题 (简单规律题)
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10146389.html
Copyright © 2011-2022 走看看