zoukankan      html  css  js  c++  java
  • [BZOJ3812]主旋律

    [BZOJ3812]主旋律

    Description

    响应主旋律的号召,大家决定让这个班级充满爱,现在班级里面有 n 个男生。如果 a 爱着 b,那么就相当于 a 和 b 之间有一条 a→b 的有向边。如果这 n 个点的图是强联通的,那么就认为这个班级是充满爱的。不幸的是,有一些不好的事情发生了,现在每一条边都可能被摧毁。我作为爱的使者,想知道有多少种摧毁的方式,使得这个班级任然充满爱呢?(说人话就是有多少边的子集删去之后整个图仍然强联通。)

    Input

    第一行两个数 n 和 m,表示班级里的男生数和爱的关系数。接下来 m 行,每行两个数 a 和 b,表示男生 a 爱着男生 b。同时 a 不等于 b。所有男生从 1 到 n 标号。同一条边不会出现两遍,但可能出现 a 爱着 b,b 也爱着 a 的情况,这是两条不同的边。

    Output

    输出一行一个整数,表示对 (10^9+7) 取模后的答案。

    Sample Input

    5 15
    4 3
    4 2
    2 5
    2 1
    1 2
    5 1
    3 2
    4 1
    1 4
    5 4
    3 4
    5 3
    2 3
    1 5
    3 1

    Sample Output

    9390

    HINT

    对于 100% 的数据满足: (nleq 15,0leq mleq n)

    试题分析

    首先考虑(f_i)表示(i)点集的强连通图个数,但是发现这样不止需要添加一个点去找其与另外一个集合的连边,还需要考虑构成环的情况。
    那么我们可以反向考虑(f_i)表示(i)点集强连通图的个数,(non_i)表示(i)点集非强联通图个数。
    由此可知,(f_i=2^{s_i}-non_i),其中(s_i)表示i点集中的边数,只需要关心(non_i)了。
    这里尝试着从答案性质的角度进行状态的定义,发现最后的 不合法的 答案一定是一个缩点之后的拓扑图,也就是说必定存在出度和入度为0的强连通分量,且强连通分量个数不为1。
    因为定义出度或入度为0都是一样的,我们这里不妨考虑存在入度为0且有多个强连通分量的图的个数。
    (g_i)表示点集为i的满足如上条件图的个数,那么直观的方程就是(g_i=sum_{jsubset i} g_{i-j} imes f_j),边数的话我们在f转移时考虑。
    到这里就出现了一个问题,(g_{i-j})中也会包含入度为0的强连通分量,会重复计算。
    那么只需要奇偶容斥一下,令(g_i=-sum_{jsubset i} g_{i-j} imes f_j),于是得到(f_i=2^{s_i}-sum_{jsubset i} 2^{s_i-e_{i,j}} g_j)
    转移即可。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
     
    using namespace std;
    #define LL long long
     
    inline LL read(){
        LL x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const LL INF = 2147483600;
    const LL MAXN = 100010;
    const LL Mod = 1000000007LL;
     
    LL n,m;LL cnt[MAXN+1]; LL Ins[MAXN+1],out[MAXN+1],bin[MAXN+1],in[MAXN+1],s[MAXN+1];
    LL g[MAXN+1],f[MAXN+1];
    inline void calc(LL now,LL i){
        if((now-1)&i) calc((now-1)&i,i);
        Ins[now]=Ins[now-(now&(-now))]+cnt[in[now&(-now)]&i];
    }
     
    int main(){
        //freopen(".in","r",stdin);
        //freopen(".out","w",stdout);
        n=read(),m=read(); bin[0]=1;
        for(LL i=1;i<=m;i++){
            LL u=read(),v=read();
            in[1<<(v-1)]=in[1<<(v-1)]|(1<<(u-1)); 
            out[1<<(u-1)]=out[1<<(u-1)]|(1<<(v-1));
            bin[i]=bin[i-1]*2LL%Mod; 
        } for(LL i=1;i<(1<<n);i++) cnt[i]=cnt[i-(i&(-i))]+1;
        for(LL i=1;i<(1<<n);i++){
            calc(i,i); s[i]=s[i-(i&(-i))]+cnt[out[i&(-i)]&i]+cnt[in[i&(-i)]&i]; f[i]=bin[s[i]];
            for(LL j=i-(i&(-i));j;j=(j-1)&(i-(i&-i))) g[i]=(g[i]-f[i^j]*g[j]%Mod+Mod)%Mod;
            for(LL j=i;j;j=(j-1)&i) f[i]=(f[i]-bin[s[i]-Ins[j]]*g[j]%Mod+Mod)%Mod;
            g[i]=(g[i]+f[i])%Mod;
        } printf("%lld
    ",f[(1<<n)-1]);
        return 0;
    }
    
  • 相关阅读:
    git 无法提交到远程服务器【转载】
    vscode 常用快捷键
    mongodb nodejs一个有自增id的功能
    C++ lambda表达式与函数对象
    TypeScript的async, await, promise,多参数的调用比较(第2篇)
    了解TypeScript的async,await,promise(第1篇)
    TyepScript判断一个变量是null, or undefined
    MongoClient 对 Mongodb的 增删改查 操作
    TypeScript第一个Promise程序
    C++基类的继承和多态
  • 原文地址:https://www.cnblogs.com/wxjor/p/9445876.html
Copyright © 2011-2022 走看看