小星星「ZJOI2016」

题意

给定一个图,以及一颗树。求将树上的节点重新编号后,能够被图覆盖的种类数?节点个数≤17。


思路

考虑朴素的树上dp。

设子状态为\(dp[i][j][s]\),表示节点i的编号为j,包含子树所构成的点集为s的种类数。

转移的时间复杂度为\(O(n3^n)\),完美被卡。

考虑题目给出的限制条件:

  1. 一个节点只能被编一次号。

  2. 树上每一条边都必须在原图中出现。

如果没有第一个限制,那么我们就可以消掉第三维,转移方程如下:\[dp[i][j]=\Pi_{k\in subtree} \sum_{t=1}^n dp[k][t]\]

在此基础上,我们再考虑第一个限制条件。

当一个节点被重复编号时,肯定会有其他的节点没有被编号,那么我们可以每次枚举哪些点没有被编号。

最后统计答案的时候0个没有-1个没有+2个没有-。。。

总共的时间复杂度是\(O(n2^n)\),刚好跑过。

代码

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

    template<typename T>inline void read (T &x) {
        x=0;T f=1;char c=getchar();
        for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
        for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
        x*=f;
    }

    template<typename T>inline void write (T x) {
        if (x<0) putchar('-'),x*=-1;
        if (x>=10) write(x/10);
        putchar(x%10+'0');
    }

}

using namespace StandardIO;

namespace Project {
    #define int long long
    
    const int N=20;
    
    int n,m,ans;
    int cnt;
    int head[N];
    struct node {
        int from,to,next;
    } edge[N<<1];
    int G[N][N];
    int left[N],dp[N][N];
    
    inline int count (int x) {
        int res=0;
        while (x) {
            if (x&1) ++res;
            x>>=1;
        }
        return res;
    }
    inline void add (int a,int b) {
        edge[++cnt].from=a,edge[cnt].to=b,edge[cnt].next=head[a],head[a]=cnt;
    }
    void dfs (int now,int fa) {
        for (register int i=1; i<=n; ++i) {
            if (left[i]) dp[now][i]=0;
            else dp[now][i]=1;
        }
        for (register int i=head[now]; i; i=edge[i].next) {
            int to=edge[i].to;
            if (to==fa) continue;
            dfs(to,now);
            for (register int j=1; j<=n; ++j) {
                if (left[j]) continue;
                int tmp=0;
                for (register int k=1; k<=n; ++k) {
                    if (G[j][k]&&!left[k]) tmp+=dp[to][k];
                }
                dp[now][j]*=tmp;
            }
        }
    }
    
    inline void MAIN () {
        read(n),read(m);
        for (register int i=1; i<=m; ++i) {
            int x,y;read(x),read(y);
            G[x][y]=G[y][x]=1;
        }
        for (register int i=1; i<n; ++i) {
            int x,y;read(x),read(y);
            add(x,y),add(y,x);
        }
        for (register int s=0; s<(1<<n); ++s) {
            for (register int i=0; i<n; ++i) {
                left[i+1]=s&(1<<i);
            }
            dfs(1,1);
            int tmp=0;
            for (register int i=1; i<=n; ++i) {
                tmp+=dp[1][i];
            }
            if (count(s)%2==0) ans+=tmp;
            else ans-=tmp;
        }
        write(ans);
    }
    
    #undef int
}

int main () {
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    Project::MAIN();
}

猜你喜欢

转载自www.cnblogs.com/ilverene/p/11479578.html
今日推荐