uoj185 [ZJOI2016]小星星 【dp + 容斥】

题目链接

uoj185

题解

\(f[i][j]\)表示\(i\)为根的子树,\(i\)好点对应图上\(j\)号点时的方案数
显然这样\(dp\)会使一些节点使用同一个节点,此时总的节点数就不满\(n\)

我们枚举选的点\(S\),再进行\(dp\)
然后根据选的点数量进行容斥

【BZOJ卡不过QAQ】

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
#define lbt(x) (x & -x)
using namespace std;
const int maxn = 18,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int n,m,fa[maxn],S,G[maxn][maxn],g[maxn][maxn];
LL f[maxn][maxn];
void dfs(int u){
    LL sum;
    for (int i = 1; i <= n; i++)
        if ((S & (1 << i - 1))) f[u][i] = 1;
        else f[u][i] = 0;
    for (int to = 1; to <= n; to++) if (g[u][to] && to != fa[u]){
        fa[to] = u; dfs(to);
        for (int i = 1; i <= n; i++) if (f[u][i]){
            sum = 0;
            for (int j = 1; j <= n; j++)
                sum += f[to][j] * G[i][j];
            f[u][i] *= sum;
        }
    }
}
int main(){
    n = read(); m = read(); int a,b;
    REP(i,m){
        a = read(); b = read();
        G[a][b] = G[b][a] = 1;
    }
    for (int i = 1; i < n; i++){
        a = read(); b = read();
        g[a][b] = g[b][a] = 1;
    }
    int maxv = (1 << n) - 1,cnt;
    LL sum,ans = 0;
    for (S = 1; S <= maxv; S++){
        cnt = 0; for (int i = S; i; i -= lbt(i)) cnt++;
        dfs(1); sum = 0;
        for (int i = 1; i <= n; i++) sum += f[1][i];
        if ((n - cnt) & 1) ans -= sum;
        else ans += sum;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Mychael/p/9044690.html