【GDOI2017模拟一试4.11】颜色树(容斥||点剖||DP)

来源:互联网 发布:摄像头美化软件 编辑:程序博客网 时间:2024/05/21 22:33

Description

思源湖畔有一棵树,那是独孤玉溪最喜欢的地方。
传说中,这棵不见边际的树有N个节点,每个节点都有1片叶子,每片叶子都拥有K种颜色中的一种,独孤玉溪喜欢爬到这棵树上,沿着一条路线摘叶子,并拥有所有颜色的叶子。
独孤玉溪会选择一个起点,并沿着树边走,然后最终停在一个终点上(起点和终点可能相同),当然了每一个结点只能经过一次(每一片叶子只能摘一遍)。独孤玉溪突生奇想,有多少种不同的方案能满足自己呢?(两种方案不同当且仅当起点不同或终点不同)。

Solution

这题其实用点剖非常的好做,用DP卡卡空间也可以过,但是有一个可以过有非常短的算法——容斥。
既然最后要所有颜色都选上,那么我们枚举不选的集合,然后容斥一下就好了。

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)#define rep(i,a) for(i=first[a];i;i=next[i])using namespace std;typedef long long ll;const int maxn=5e4+7;int i,j,k,l,t,n,m;int a[maxn],first[maxn*2],last[maxn*2],next[maxn*2],num;int w[1025],er[17],p,ga,size[maxn];ll ans,ans1;void add(int x,int y){last[++num]=y,next[num]=first[x],first[x]=num;}void dfs(int x,int y){    int i,j;    size[x]=0;    if((a[x]&ga)==a[x])size[x]=1;    rep(i,x){        if(last[i]!=y){            dfs(last[i],x);            if((a[x]&ga)==a[x])size[x]+=size[last[i]];            else if(size[last[i]])ans1+=size[last[i]]*(size[last[i]]-1);        }    }    if(x==1&&size[x])ans1+=(ll)size[x]*(size[x]-1);}int main(){    freopen("colortree.in","r",stdin);    freopen("colortree.out","w",stdout);//  freopen("fan.in","r",stdin);    er[0]=1;fo(i,1,16)er[i]=er[i-1]*2;    scanf("%d%d",&n,&m);    fo(i,1,n)scanf("%d",&a[i]),a[i]=er[a[i]-1];    fo(i,1,n-1){        scanf("%d%d",&k,&l);        add(k,l),add(l,k);    }    p=er[m]-1;    fo(i,1,p){        fo(j,1,m)if(i&er[j-1])w[i]++;        w[i]=(m+1-w[i])%2;if(!w[i])w[i]=-1;    }    fo(i,1,p){        ga=i;ans1=0;        dfs(1,0);        ans+=ans1*w[i];    }    if(m==1)ans+=n;    printf("%lld\n",ans);}
1 0