BZOJ3812主旋律

/*
这道题其实没有看懂  所以整理一下吧
首先思想转化成所有方案减去不强联通的方案
不强联通的方案相当于很多强联通分量缩点后的dag
转化成子问题, 问很多点的dag方案数
然后枚举作为出度为0的点集 T, 然后S - T和T之间的边是随便连的
但是由于S-T中你不能保证不包含出度为0的点, 所以要容斥
最后得到一个式子
f(S) = \sum{T \belong S T != kongji}  (-1) ^ {|T| - 1} f(S - T) * 2 ^{way(S - T, T)}
ways 函数我们可以通过预先状压一遍求出来
但是这样再转化成原来问题我们需要枚举强联通分量, 显然复杂度不对

然后我们考虑上面那个dp实际上的贡献
我们枚举所有没有出边的强联通分量缩成的点集合T, 假如T中的点组成奇数个强联通分量, 那么对于答案的贡献系数就是1, 否则是-1
用g(S)表示将S分成若干个强联通分量的方案数, 当然这里是要合并进去系数的, F(S)表示S的强联通子图的个数
然后就可以得到G(S) =  F(S) - \sum{T\belong S, u \ T} F(T) g(S - T) (为啥总感觉有一种反演思想)
那么
F(S) = 2 ^ (h(S)) - \sum{T \belong S T != 0} 2 ^ way(T, S - T) + (h(S - T)) g(T)
然后子集dp就好了




*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
#define ll long long
#define M 15
#define mmp make_pair
using namespace std;
int read() {
    int nm = 0, f = 1;
    char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
    return nm * f;
}
const int mod = 1000000007;
void add(int &x, int y) {
    x += y;
    x -= x >= mod ? mod : 0;
    x += x < 0 ? mod : 0;
}
int poww[100000], f[1 << M], g[1 << M], h[1 << M], p[1 << M], in[1 << M], out[1 << M], bit[1 << M], n, m;
int main() {
    n = read(), m = read();
    poww[0] = 1;
    for(int i = 1; i < n * n; i++) poww[i] = (poww[i - 1] << 1) % mod;
    for(int i = 1; i < 1 << n; i++) bit[i] = bit[i - (i & -i)] + 1;
    for(int i = 1; i <= m; i++) {
        int x = read(), y = read();
        x = 1 << (x - 1), y = 1 << (y - 1);
        out[x] |= y, in[y] |= x;
    }
    for(int s = 1; s < 1 << n; s++) {
        int mad = s & -s, outside = s ^ mad;
        for(int i = outside; i; i = (i - 1) & outside)
            add(g[s], -1ll * f[s ^ i] * g[i] % mod);
        h[s] = h[outside] + bit[in[mad] & outside] + bit[out[mad] & outside];
        f[s] = poww[h[s]];
        for(int i = s; i; i = (i - 1) & s) {
            if(i != s) {
                int one = (i ^ s) & -(i ^ s);
                p[i] = p[i ^ one] + bit[out[one] & i] - bit[in[one] & (i ^ s)]; 
            } else p[i] = 0;
            add(f[s], -1ll * poww[h[s ^ i] + p[i]] * g[i] % mod);
        }
        add(g[s], f[s]);
    }
    cout << f[(1 << n) - 1] << "\n";
    return 0;
}

 

转载于:https://www.cnblogs.com/luoyibujue/p/10505291.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值