树上独立集数量 树型DP
题目描述:
对于一棵树,独立集是指两两互不相邻的节点构成的集合。例如,图1有5个不同的独立集(1个双点集合、3个单点集合、1个空集),图2有14个不同的独立集,图3有5536个不同的独立集。
输入:
第一行一个正整数n,表示点的数量。n最大为100000。
接下来n-1行,有两个整数a、b,表示编号为a、b的两个点之间有一条边,其中a、b大于等于1,小于等于n。
17
1 2
1 3
2 4
2 5
3 6
3 7
5 8
5 9
7 10
7 11
8 12
8 13
10 14
10 15
12 16
15 17
输出:
输出一行,包含一个整数,表示独立集的数量。由于这个数很大,你只需要输出这个数除以10081的余数。
分析:
这是一道树形DP。设f[i][0]表示在选i个节点的独集数,f[i][1]表示不选第i个节点的独集数。
很明显,每一个节点的f都是有它的子节点得到的。
如果选了第i个节点,那么它的子节点全部不选。所以,f[i][0]就等于它的子节点f[j][1]的乘积。
反之,不选第i个节点,那么它的子节点可以选也可以不选,f[i][1]等于它的子节点(f[j][0] +f[j][1])的乘积。
边就用奇怪的方法维护就行了(不要告诉我你不会前向新或边集数组)。
#include<cstdio> #include<cstring> #include<algorithm> const int mo=10081; int n,a[300001][2],g[300001],f[300001][2]; bool bz[300001]; void qsort1(int l, int r) { int i=l; int j=r; int x; int t; x=a[(l+r)/2][0]; while (i<=j) { while(a[i][0]<x) i++; while(a[j][0]>x) j--; if(i<=j) { t=a[j][0]; a[j][0]=a[i][0]; a[i][0]=t; t=a[j][1]; a[j][1]=a[i][1]; a[i][1]=t; j--; i++; } } if (l<j) qsort1(l,j); if (r>i) qsort1(i,r); } void dfs(int x) { int z1,z2,z=g[x],l=0; while (a[z][0]==x) { if (bz[a[z][1]]==false) { l=1; bz[a[z][1]]=true; dfs(a[z][1]); f[x][0]=(f[x][0]*f[a[z][1]][1])%mo; if (z==g[x]) { z1=f[a[z][1]][0]; z2=f[a[z][1]][1]; } else { z1=(z1*f[a[z][1]][0]+z1*f[a[z][1]][1])%mo; z2=(z2*f[a[z][1]][0]+z2*f[a[z][1]][1])%mo; } } z++; } if (l==1) { f[x][1]=(z1+z2)%mo; } } int main() { int i,j,k; scanf("%d",&n); int zl=0; for (i=1;i<=n-1;i++) { int x,y; scanf("%d%d",&x,&y); zl++; a[zl][0]=x; a[zl][1]=y; zl++; a[zl][0]=y; a[zl][1]=x; } for (i=1;i<=n;i++) { f[i][1]=1; f[i][0]=1; } qsort1(1,zl); int z=1; for (i=1;i<=zl;i++) { if (a[z][0]!=a[i][0]) { g[a[z][0]]=z; z=i; } } g[a[z][0]]=z; bz[1]=true; dfs(1); printf("%d",(f[1][0]+f[1][1])%mo); }