zoukankan      html  css  js  c++  java
  • 题解 P4336 [SHOI2016]黑暗前的幻想乡

    题解

    前置芝士 :矩阵树定理

    本题是一道计数题,有两个要求:

    1. 建造的公路构成一颗生成树

    2. 每条公路由不同的公司建造,每条公路与一个公司一一映射

    那么看到这两个要求后,我们很容易想到第一个条件用矩阵树定理,那么对于第二个条件,我们就很容易想到容斥原理。

    先不考虑第二个条件,把所有边都加进去(没有自环),这是我们用矩阵树原理算出来的结果不仅有 (n-1) 个公司建造的方案,也包括了 ((n-2)...1) 个公司建造的方案。

    此时,我们需要减去 (n-2) 个公司建造的方案,那么这里我们就把其中一个公司去掉,再进行计算,注意这里去掉一个公司有 (n-1) 种方案。

    但是我们会发现 (n-3) 个公司建造的方案被重复减去了,所以我们需要加回来,至此,就是一个纯的容斥了。

    对于删去不同的公司,计算不同的方案,我们可以用二进制压一下 (n-1) ,二进制每一位 (1) 代表选取这一位代表的公司。

    而在求解行列式的过程中,我们可以直接利用逆元进行求解,也可以辗转相除。

    所以最后前一种复杂度为 (mathcal O(2^{n-1}((n-1)^3+(n-1)log(1e9+7)))) 后一种的复杂度为 (mathcal O(2^{n-1}(n-1)^3logn))

    Code

    逆元 (AC kern 0.4em CODE:)

    #include<bits/stdc++.h>
    #define ri register signed
    #define p(i) ++i
    using namespace std;
    namespace IO{
        char buf[1<<21],*p1=buf,*p2=buf;
        #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
        inline int read() {
            ri x=0,f=1;char ch=gc();
            while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
            while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
            return x*f; 
        }
    }
    using IO::read;
    namespace nanfeng{
        #define lm(x) (1<<x)
        #define lowbit(x) (x&-(x))
        #define cmax(x,y) ((x)>(y)?(x):(y))
        #define cmin(x,y) ((x)>(y)?(y):(x))
        #define FI FILE *IN
        #define FO FILE *OUT
        typedef long long ll;
        static const int N=20,MOD=1e9+7;
        int num[N],u[N][N*N],v[N][N*N],G[N][N],siz[lm(16)+7],lg[lm(16)+7]={-1},n,cnt,st,ans;
        inline void add(int u,int v) {p(G[u][v]),p(G[v][u]);}
        inline int fpow(int x,int y) {
            int res=1;
            while(y) {
                if (y&1) res=1ll*res*x%MOD;
                x=1ll*x*x%MOD;y>>=1;
            }
            return res;
        }
        inline int Gauss() {
            int res=1,tr=0;
            for (ri i(1);i<=cnt;p(i)) {
                for (ri j(i+1);j<=cnt;p(j)) if (G[j][i]) {swap(G[i],G[j]);tr^=1;break;} 
                int inv=fpow(G[i][i],MOD-2);
                for (ri j(i+1);j<=cnt;p(j)) {
                    int tmp=1ll*inv*G[j][i]%MOD;
                    for (ri k(i+1);k<=cnt;p(k)) G[j][k]=(G[j][k]-1ll*G[i][k]*tmp%MOD+MOD)%MOD;
                }
                if (!G[i][i]) return 0;
                res=1ll*res*G[i][i]%MOD;
            }
            return tr?-res:res;
        }
        inline int main() {
            // FI=freopen("nanfeng.in","r",stdin);
            // FO=freopen("nanfeng.out","w",stdout);
            n=read(),cnt=n-1;st=(1<<n-1)-1;
            for (ri i(1);i<=st;p(i)) siz[i]=siz[i>>1]+(i&1),lg[i]=lg[i>>1]+1;
            for (ri i(1);i<n;p(i)) {
                num[i]=read();
                for (ri j(1);j<=num[i];p(j)) u[i][j]=read(),v[i][j]=read();
            }
            for (ri i(1);i<=st;p(i)) {
                int low=i;
                memset(G,0,sizeof(G));
                while(low) {
                    int id=lg[lowbit(low)]+1;
                    for (ri j(1);j<=num[id];p(j)) add(u[id][j],v[id][j]);
                    low-=lowbit(low);
                }
                for (ri j(1);j<=n;p(j)) {
                    for (ri k(1);k<=n;p(k)) if (j^k) G[j][j]+=G[j][k],G[j][k]=-G[j][k];
                }
                // for (ri j(1);j<=n;p(j)) {
                //     for (ri k(1);k<=n;p(k)) printf("%d ",G[j][k]);
                //     puts("");
                // }
                int tmp=(Gauss()+MOD)%MOD;
                // printf("state=%d tmp=%d
    ",i,tmp);
                ans=((ll)ans+MOD+((n-siz[i])&1?tmp:-tmp))%MOD;
            }
            printf("%d
    ",ans);
            return 0;
        }
    }
    int main() {return nanfeng::main();}
    
    Code

    辗转相除法 (AC kern 0.4em CODE:)

    #include<bits/stdc++.h>
    #define ri register signed
    #define p(i) ++i
    using namespace std;
    namespace IO{
        char buf[1<<21],*p1=buf,*p2=buf;
        #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
        inline int read() {
            ri x=0,f=1;char ch=gc();
            while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
            while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
            return x*f; 
        }
    }
    using IO::read;
    namespace nanfeng{
        #define lm(x) (1<<x)
        #define lowbit(x) (x&-(x))
        #define cmax(x,y) ((x)>(y)?(x):(y))
        #define cmin(x,y) ((x)>(y)?(y):(x))
        #define FI FILE *IN
        #define FO FILE *OUT
        typedef long long ll;
        static const int N=20,MOD=1e9+7;
        int num[N],u[N][N*N],v[N][N*N],G[N][N],siz[lm(16)+7],lg[lm(16)+7]={-1},n,cnt,st,ans;
        inline void add(int u,int v) {p(G[u][v]),p(G[v][u]);}
        inline int Gauss() {
            int res=1,tr=0;
            for (ri i(1);i<=cnt;p(i)) {
                for (ri j(i+1);j<=cnt;p(j)) {
                    while(G[j][i]) {
                        int k=G[i][i]/G[j][i];
                        for (ri l(i);l<=cnt;p(l)) G[i][l]=(G[i][l]-(ll)G[j][l]*k%MOD)%MOD;
                        swap(G[i],G[j]);tr^=1;
                    }
                }
                if (!G[i][i]) return 0;
                res=(ll)res*G[i][i]%MOD;
            }
            return tr?-res:res;
        }
        inline int main() {
            // FI=freopen("nanfeng.in","r",stdin);
            // FO=freopen("nanfeng.out","w",stdout);
            n=read(),cnt=n-1;st=(1<<n-1)-1;
            for (ri i(1);i<=st;p(i)) siz[i]=siz[i>>1]+(i&1),lg[i]=lg[i>>1]+1;
            for (ri i(1);i<n;p(i)) {
                num[i]=read();
                for (ri j(1);j<=num[i];p(j)) u[i][j]=read(),v[i][j]=read();
            }
            for (ri i(1);i<=st;p(i)) {
                int low=i;
                memset(G,0,sizeof(G));
                while(low) {
                    int id=lg[lowbit(low)]+1;
                    for (ri j(1);j<=num[id];p(j)) add(u[id][j],v[id][j]);
                    low-=lowbit(low);
                }
                for (ri j(1);j<=n;p(j)) {
                    for (ri k(1);k<=n;p(k)) if (j^k) G[j][j]+=G[j][k],G[j][k]=-G[j][k];
                }
                // for (ri j(1);j<=n;p(j)) {
                //     for (ri k(1);k<=n;p(k)) printf("%d ",G[j][k]);
                //     puts("");
                // }
                int tmp=(Gauss()+MOD)%MOD;
                // printf("state=%d tmp=%d
    ",i,tmp);
                ans=((ll)ans+MOD+((n-siz[i])&1?tmp:-tmp))%MOD;
            }
            printf("%d
    ",ans);
            return 0;
        }
    }
    int main() {return nanfeng::main();}
    
  • 相关阅读:
    今日总结
    微任务与宏任务
    20171128微信小程序
    20171128-微信小程序之点餐
    git
    第二次学习Javascript笔记
    base64图片
    网页布局基础-css版
    StuQ技能图谱——前端
    前端开发工具
  • 原文地址:https://www.cnblogs.com/nanfeng-blog/p/14880720.html
Copyright © 2011-2022 走看看