zoukankan      html  css  js  c++  java
  • BZOJ4671异或图

    题目描述

    定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与
    G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中.
    现在给定 s 个结点数相同的图 G1...s, 设 S = {G1, G2, . . . , Gs}, 请问 S 有多少个子集的异
    或为一个连通图?
    题解
    先考虑一个dp。
    对于这种连通性问题的dp我们通常是设一个f数组一个g数组,然后找到这两个数组的关系。
    我们定义g[i]表示恰好有i个联通块的方案数(是指把点集分割成i个集合,不同集合之间不能连边,相同集合之间连通)。
    考虑再设置f[i]表示至少有i个联通块的方案数(同上,只是相同集合之间也可以不连通)。
    那么我们的转移有:f[i]=∑s(j,i)g[j]
    由于我们要求g,就斯特林反演一下。
    g[i]=∑(-1)j-iS(j,i)f[j]
    其实我们只需要求g[1]
    g[1]=∑(-1)i-1S(i,1)f[i]
    g[1]=∑(-1)i-1(i-1)!f[i]
    我们可以以贝尔数的复杂度枚举这n个点的集合划分,然后算出有多少种图的选择方案可以保证不同集合之间没有连边。
    对于每一条跨越集合中的边,我们可以写出一个方程。
    a1G1^a2G2^a3G3.....=0
    ai为0/1表示这条边是否在Gi这张图中存在。
    它显然是有解的(全取0就可以了
    我们要做的是求解得个数,答案为2自由元
    我们可以用线性基来模拟高斯消元的过程,自由元的个数就是最后线性基中0的个数。
    代码
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll jie[11],be[11],n,G,x[63],ans;
    char s[202];
    int g[62][11][11];
    inline int rd(){
        int x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    void solve(int now,int cnt){
        if(now>n){
            for(int i=1;i<=n;++i)
              for(int j=i+1;j<=n;++j)if(be[i]!=be[j]){
                  ll tmp=0;
                  for(int k=1;k<=G;++k)tmp|=(1ll*g[k][i][j]<<k-1);
                for(int k=G-1;k>=0;--k)if(tmp&(1ll<<k)){
                    if(!x[k]){x[k]=tmp;break;}
                    tmp^=x[k];
                } 
              }
            int num=0;
            for(int i=0;i<G;++i)if(x[i])num++;
            ans+=((cnt&1)?1:-1)*(1ll<<(G-num))*jie[cnt-1];
            for(int i=0;i<G;++i)x[i]=0;
        }
        else{
         for(int j=1;j<=cnt+1;++j){be[now]=j;solve(now+1,max(cnt,j));}
       }
    }
    int main(){
        G=rd();
        for(int i=1;i<=G;++i){
            scanf("%s",s+1);
            n=strlen(s+1);
            n=(1+sqrt(1+8*n))/2;int now=1;
            for(int j=1;j<=n;++j)for(int k=j+1;k<=n;++k){g[i][j][k]=s[now]-'0';now++;} 
        } 
        jie[0]=1;
        for(int i=1;i<=n;++i)jie[i]=jie[i-1]*i;
        solve(1,0);
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    Servlet的建立以及配置使用
    maven 安装 及其 创建
    错误总结
    使用分层实现业务处理
    JSP数据交互(三)
    JSP数据交互(二)
    JSP数据交互(一)
    动态网页开发基础
    记录mysql编码问题
    .net core 2.0 升级 2.1 访问mysql出现bug解决
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10396736.html
Copyright © 2011-2022 走看看