bzoj3162 独钓寒江雪 树Hash 树dp 组合数学
Posted Loser Of Life
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3162 独钓寒江雪 树Hash 树dp 组合数学相关的知识,希望对你有一定的参考价值。
链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3162
题意:给出一棵无根树,求出本质不同独立集数目。
这道题真是一道好题……无限$orz$ $VFleaKing$……对着题解看了半天才看明白明明是你太蒻了……
好了不废话直接切入正题。首先我们需要找出一个方式使得所有的同构树形态都一样,怎么办呢,找到这个树的中心,以这个中心为根重新搞。
但是怎么搞出中心呢……首先,中心一定在整棵树最长的链上。那么我们先随意以一个点为起点广搜一次找到最远点,然后以这个点为起点再广搜一次,这两个最远的点就是树上最远点对。
然后我们就一点一点往回缩……缩到中点就是中心……但是可能有一个问题……就是这个中点可能在边上……这时候我们就需要接出一个虚拟节点做根……
然后,我们就要考虑求解了。如果说只求独立集数目那很简单树形$DP$即可……然而这个题目还有一个条件:同构树算同一种……因此我们还要判一下树同构……所以我们还需要判同构……于是我又现学了树$Hash$……
然后我们可以发现,本质不同的方案数就有$C(k,p+k-1)$种……
然后儿子列表就可以改变……变为同一结构出现了多少次……
于是我们可以把正常的独立集公式变一下:
($f[]$表示选这个节点,$g[]$表示不选这个节点)
二次项系数直接$C(m,n)$就好了……
接下来分类讨论……有中点时,方案数为$f[root]+g[root]$……没有时,由于两个端点不能同时选中,所以还需要继续分类讨论……直接上代码吧……
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 const int maxn=500005,mod=(int)1e9+7,mod2=(int)1e9+9,inf=0x3f3f3f3f; 8 struct node 9 { 10 int to,next; 11 }edge[maxn<<1]; 12 int head[maxn],tot=-1; 13 void addedge(int u,int v) 14 { 15 edge[++tot]=(node){v,head[u]};head[u]=tot; 16 } 17 int n,dis[maxn],from[maxn]; 18 int q[maxn],h,t; 19 int bfs(int x) 20 { 21 static bool vis[maxn];fill(vis,vis+n+1,0);fill(dis,dis+n+1,inf);h=t=0; 22 vis[x]=1,dis[x]=0,q[++t]=x; 23 while(h<t) 24 { 25 int now=q[++h]; 26 for(int i=head[now];i!=-1;i=edge[i].next) 27 { 28 int v=edge[i].to; 29 if(!vis[v])dis[v]=dis[now]+1,q[++t]=v,vis[v]=1,from[v]=i; 30 } 31 } 32 for(int i=1;i<=n;i++) 33 if(dis[i]>dis[x])x=i; 34 return x; 35 } 36 inline long long qpow(long long x,int tim) 37 { 38 long long tmp=1; 39 for(;tim;tim>>=1,x=x*x%mod) 40 if(tim&1)tmp=tmp*x%mod; 41 return tmp; 42 } 43 long long inv[maxn]; 44 void pre() 45 { 46 inv[1]=1; 47 for(int i=2;i<=n;i++)inv[i]=(mod-inv[mod%i])*(mod/i)%mod; 48 } 49 inline long long C(long long x,long long y) 50 { 51 if(x<0)x+=mod;if(x>=mod)x-=mod;long long ans=1; 52 for(int i=1;i<=y;i++)ans=(ans*(x+1-i))%mod,ans=(ans*inv[i])%mod; 53 return ans; 54 } 55 int root,son[maxn],cp,size[maxn];long long Has[maxn]; 56 inline bool cmp(int x,int y) 57 { 58 return Has[x]<Has[y]; 59 } 60 inline void getson(int now) 61 { 62 cp=0; 63 for(int i=head[now];i!=-1;i=edge[i].next) 64 { 65 int v=edge[i].to; 66 if(dis[v]==dis[now]+1)son[++cp]=v; 67 } 68 sort(son+1,son+cp+1,cmp); 69 } 70 long long Gethash(int now) 71 { 72 getson(now);long long ans=1; 73 for(int i=1;i<=cp;i++)ans=(ans+qpow(2,Has[son[i]]))%mod,ans=ans*Has[son[i]]%mod; 74 return ans; 75 } 76 void Pre() 77 { 78 for(int i=t;i>=1;i--) 79 { 80 int j=i; 81 while(i>1&&dis[q[i]]==dis[q[i-1]])i--; 82 for(int k=i;k<=j;k++)Has[q[k]]=Gethash(q[k]); 83 } 84 } 85 long long f[maxn],g[maxn]; 86 void solve() 87 { 88 for(int i=t;i;i--) 89 { 90 int now=q[i];f[now]=g[now]=1;getson(now);size[now]=1; 91 for(int j=1;j<=cp;j++)size[now]+=size[son[j]]; 92 for(int j=1;j<=cp;j++) 93 { 94 int k=j; 95 while(j<cp&&Has[son[j+1]]==Has[son[j]])j++; 96 f[now]=(f[now]*C(g[son[j]]+j-k,j-k+1))%mod,g[now]=(g[now]*C(f[son[j]]+g[son[j]]+j-k,j-k+1))%mod; 97 } 98 } 99 } 100 int haha() 101 { 102 scanf("%d",&n);memset(head,-1,sizeof(head)); 103 for(int i=1;i<n;i++) 104 { 105 int x,y;scanf("%d%d",&x,&y); 106 addedge(x,y);addedge(y,x); 107 } 108 int x=bfs(1),y=bfs(x),l=dis[y],pos=y; 109 while(dis[pos]>((l>>1)+1))pos=edge[from[pos]^1].to; 110 int lpos; 111 if(l&1) 112 { 113 n++; 114 lpos=edge[from[pos]^1].to;edge[from[pos]].to=n;edge[from[pos]^1].to=n;addedge(n,pos);addedge(pos,n),addedge(n,lpos);addedge(lpos,n); 115 root=n; 116 } 117 else 118 { 119 if(n!=1)pos=edge[from[pos]^1].to; 120 root=pos; 121 } 122 bfs(root);pre();Pre();solve(); 123 if(l&1) 124 { 125 long long ans=0; 126 if(Has[pos]==Has[lpos])ans=(ans+f[pos]*g[pos]%mod),ans=(ans+C(g[pos]+1,2)); 127 else ans=(ans+f[pos]*g[lpos]%mod),ans=(ans+g[pos]*f[lpos]%mod),ans=(ans+g[pos]*g[lpos]%mod); 128 ans%=mod; 129 printf("%lld\\n",ans); 130 } 131 else printf("%lld\\n",(f[root]+g[root])%mod); 132 } 133 int sb=haha(); 134 int main(){;}
以上是关于bzoj3162 独钓寒江雪 树Hash 树dp 组合数学的主要内容,如果未能解决你的问题,请参考以下文章