Luogu3349 [ZJOI2016]小星星

Luogu3349 [ZJOI2016]小星星

题面:洛谷

解析

容斥原理好题。称原图点集为A,树的点集为B,那么题目中所求即为B中元素与A中元素一一对应且边合法的方案数,考虑容斥,设(g(S)(Ssubseteq A))表示B仅与S中元素对应的方案数(不一定对应完全),那么有:

[ans=sum_{S} (-1)^{ n-vert{S}vert }g(S) ]

现在考虑如何计算(g(S)),考虑动态规划,令1为树的根节点,设(f(i,j))表示以i为根的子树,ii与j的方案数,那么转移顺着原图中的边与树上的边即可,有:

[f(i,j)=prod_{v=i.son} sum_{k=j.to} f(v,k) ]

代码


// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#define N 20
#define LL long long
using namespace std;
inline int In(){
    char c=getchar(); int x=0,ft=1;
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
    for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
    return x*ft;
}
int n,m,S0,q[N],h1[N],h2[N],tot1=1,tot2=0,qc=0;
LL f[N][N],sum,ans;
struct E{ int to,nex; }e1[N*N],e2[N<<1];
inline void Add1(int u,int v){
    e1[++tot1]=(E){v,h1[u]}; h1[u]=tot1;
    e1[++tot1]=(E){u,h1[v]}; h1[v]=tot1;
}
inline void Add2(int u,int v){
    e2[++tot2]=(E){v,h2[u]}; h2[u]=tot2;
    e2[++tot2]=(E){u,h2[v]}; h2[v]=tot2;
}
void dfs(int u,int pre){
    for(int i=h2[u];i;i=e2[i].nex)
    if(e2[i].to!=pre) dfs(e2[i].to,u);
    for(int i=1;i<=qc;++i){
        f[u][q[i]]=1;
        for(int j=h2[u],v;j;j=e2[j].nex){
            v=e2[j].to; if(v==pre) continue; sum=0;
            for(int k=h1[q[i]],v_q;k;k=e1[k].nex){
                v_q=e1[k].to; sum+=f[v][v_q];
            }
            f[u][q[i]]*=sum;
            if(!f[u][q[i]]) break;
        }
    }
}
int main(){
    n=In(); m=In(); S0=(1<<n)-1;
    for(int i=1;i<=m;++i) Add1(In(),In());
    for(int i=1;i<n;++i) Add2(In(),In());
    for(int S=1;S<=S0;++S){
        qc=0; memset(f,0,sizeof(f));
        for(int i=1;i<=n;++i)
        if(S&(1<<(i-1))) q[++qc]=i;
        dfs(1,-1); sum=0;
        for(int i=1;i<=qc;++i) sum+=f[1][q[i]];
        ans=ans+((n-qc)&1?-sum:sum);
    }
    printf("%lld
",ans);
    return 0;
}


原文地址:https://www.cnblogs.com/pkh68/p/10581284.html