首页 > 代码库 > BZOJ 3162 独钓寒江雪 树同构+树形DP
BZOJ 3162 独钓寒江雪 树同构+树形DP
题目大意:给定一棵树,求本质不同的独立集个数对1000000007取模后的值
首先独立集个数应该都会求吧- -
令f[x][0]为x这个点不选的独立集个数
f[x][1]为x这个点选的独立集个数
那么有f[x][0]=Σf[son[x]][0]+f[son[x]][1]
f[x][1]=Σf[son[x]][0]
但是现在要求本质不同
说到本质不同我们很容易想到群论 但是群论显然写不了- -
于是我们考虑对树进行一下处理
首先将树有根化
为了保证形态相同的子树在有根化之后形态依然相同,我们需要找到这棵树的重心
如果重心是两个点,就在这两个点之间添加一个点作为根
如果随便选择一个点作为根,后果就是形态相同的部分有根化之后形态不同- -
比如说过不去2 1 2这组样例- -
而如果选择了重心,由于两个形态相同的部分大小一定相同,故重心一定不在其中之一
接下来考虑对DP方程进行一些处理
比如说有三棵形态完全相同的树,每棵都有ABCD四种方案
那么我选AAB和选BAA是等价的
n个点,涂上m种颜色,那么本质不同的方案数为C(n+m-1,n)
现在的问题就是计算形态相同的子树的个数
我们可以设计一个Hash函数对每棵子树进行哈希
Hash函数越奇葩越好- - 太简单的会有BUG
比如我的方法:
首先对点设一个初值,将子树按照哈希值排序,每次执行:
for(i=1;i<=top;i++) (((hash[x]*=BASE)+=hash[stack[i]])^=hash[stack[i]])+=hash[stack[i]];
然后就搞过了- - 随便写了一发RANK1了什么情况- - 一定是自然溢出比较快的缘故- -
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 500500 #define MOD 1000000007 #define ORIGIN 233 #define BASE 233333333 using namespace std; struct abcd{ int to,next; }table[M<<1]; int head[M],tot=1; int n,root,cgs[2],size[M]; unsigned long long hash[M]; long long inv[M],f[M][2],ans; bool Compare(int x,int y) { return hash[x] < hash[y]; } void Add(int x,int y) { table[++tot].to=y; table[tot].next=head[x]; head[x]=tot; } void DFS(int x,int from) { int i,flag=1; size[x]=1; for(i=head[x];i;i=table[i].next) { if(table[i].to==from) continue; DFS(table[i].to,x); size[x]+=size[table[i].to]; if(size[table[i].to]<<1>n) flag=0; } if(n-size[x]<<1>n) flag=0; if(flag) (cgs[0]?cgs[1]:cgs[0])=x; } long long C(long long n,long long m) { int i; long long re=1; for(n%=MOD,i=1;i<=m;i++) (re*=(n-i+1)*inv[i]%MOD)%=MOD; return re; } void Tree_DP(int x,int from) { static int stack[M]; int i,j,top=0; hash[x]=ORIGIN; for(i=head[x];i;i=table[i].next) if(table[i].to!=from) Tree_DP(table[i].to,x); for(i=head[x];i;i=table[i].next) if(table[i].to!=from) stack[++top]=table[i].to; sort(stack+1,stack+top+1,Compare); f[x][0]=f[x][1]=1; for(i=1;i<=top;i=j) { for(j=i+1;j<=top&&hash[stack[i]]==hash[stack[j]];j++); (f[x][0]*=C(j-i+f[stack[i]][0]+f[stack[i]][1]-1,j-i) )%=MOD; (f[x][1]*=C(j-i+f[stack[i]][0]-1,j-i) )%=MOD; } for(i=1;i<=top;i++) (((hash[x]*=BASE)+=hash[stack[i]])^=hash[stack[i]])+=hash[stack[i]]; } void Linear_Shaker() { int i; inv[1]=1; for(i=2;i<=n;i++) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD; } int main() { int i,x,y; cin>>n; Linear_Shaker(); for(i=1;i<n;i++) { scanf("%d%d",&x,&y); Add(x,y);Add(y,x); } DFS(1,0); if(cgs[1]) { for(i=head[cgs[0]];i;i=table[i].next) if(table[i].to==cgs[1]) { table[i].to=table[i^1].to=root=n+1; break; } Add(n+1,cgs[0]); Add(n+1,cgs[1]); } else root=cgs[0]; Tree_DP(root,0); if(!cgs[1]) ans=(f[root][0]+f[root][1])%MOD; else { x=cgs[0];y=cgs[1]; if(hash[x]!=hash[y]) ans=(f[x][0]*f[y][0]%MOD+f[x][1]*f[y][0]%MOD+f[x][0]*f[y][1]%MOD)%MOD; else ans=(f[x][0]*f[y][1]+C(f[x][0]+1,2) )%MOD; } cout<<ans<<endl; return 0; }
BZOJ 3162 独钓寒江雪 树同构+树形DP
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。