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

时间: 2024-10-05 19:40:25

bzoj3162 独钓寒江雪 树Hash 树dp 组合数学的相关文章

树Hash

我们有时需要判断一些树是否同构.这时,选择恰当的Hash方式来将树映射成一个便于储存的Hash值(一般是 32 位或 64 位整数)是一个优秀的方案. 树Hash定义在有根树上.判断无根树同构的时候,可以比较重心为根的Hash值或者比较每个点为根的Hash值. 树哈希有很多种哈希方式,下面介绍其中一种: $f_x$表示$x$为根的子树的Hash值,$son_x$表示$x$的儿子结点集合,$size_y$表示$y$为根的子树规模,$prime(i)$表示第$i$个素数,则 $$f_x = 1 +

Hash树(散列树)和Trie树(字典树、前缀树)

1.Hash树 理想的情况是希望不经过任何比较,一次存取便能得到所查的记录, 那就必须在记的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和一个唯一的存储位置相对应.因而在查找时,只要根据这个对应关系f找到 给定值K的像f(K).由此,不需要进行比较便可直接取得所查记录.在此,我们称这个对应关系为哈希(Hash)函数,按这个思想建立的表为哈希表. 在哈希表中对于不同的关键字可能得到同一哈希地址,这种现象称做冲突.在一般情况下,冲突只能尽可能地减少,而不能完全避免.因为哈希函数是从

hdu 3030 Increasing Speed Limits (离散化+树状数组+DP思想)

Increasing Speed Limits Time Limit: 2000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 481    Accepted Submission(s): 245 Problem Description You were driving along a highway when you got caught by the road p

ZOJ 3632 Watermelon Full of Water (线段树 区间更新 + dp)

题目大意: 让每天都能吃到西瓜.最少需要花多少钱. 思路分析: dp[pos] 就表示  要让 前i天每天都有西瓜吃,最少需要花多少钱. 那么如果你买这个西瓜的话.那么这个西瓜能吃的持续时间都要更新一下. 然后再在每个西瓜的更新部分取最小的,就可以是这个点所能得到的最小值. 其实就是 dp[i] = min (dp[i] , dp[ j - k +1] + a[j]); 但是枚举前面的时候会超时,就用线段树维护. 5 1 2 3 4 5 1 2 2 2 2 给出这组数据是说,每次买西瓜的时候,都

BZOJ 3012 [Usaco2012 Dec]First! wzq脑洞hash树(正解trie树)

博客风格转化计划实验篇2 题意: 给n(n<=30000)个字符串,所有字符串长度加起来不超过300000(字符串只含有小写字母). 求解哪些字符串可以称作第一字符串. 一个字符串能被称为第一字符串的条件为存在一种字典序使它排在第一名. 方法: wzq脑洞hash树-.. 然而并没有trie树优越. 并且我脑洞的这个树好处有啥:暂且不知道. 坏处有啥:很容易被卡,自带常数. 所以为什么要这么写?只是因为我跟wjc说这题我特么一定要用带hash的东西搞过去! *目标达成√ 解析: 对于以下内容,请

hdu4455之树状数组+DP

Substrings Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1571    Accepted Submission(s): 459 Problem Description XXX has an array of length n. XXX wants to know that, for a given w, what is

HDU 5886 Tower Defence(2016青岛网络赛 I题,树的直径 + DP)

题目链接  2016 Qingdao Online Problem I 题意  在一棵给定的树上删掉一条边,求剩下两棵树的树的直径中较长那的那个长度的期望,答案乘上$n-1$后输出. 先把原来那棵树的直径求出来.显然删掉的边不是这条直径上的边,那么这时答案就是这条直径的长度. 否则就是直径的某个端点到某一个点(要求连通)的距离的最大值. 在整条链上做两次$DP$之后枚举取较大值即可. #include <bits/stdc++.h> using namespace std; #define r

HDU 6447 YJJ’s Salesman (树状数组 + DP + 离散)

题意: 二维平面上N个点,从(0,0)出发到(1e9,1e9),每次只能往右,上,右上三个方向移动, 该N个点只有从它的左下方格点可达,此时可获得收益.求该过程最大收益. 分析:我们很容易就可以想到用DP,假设这个位置是相对上一个位置的方向而来,但是复杂度达到N^2 ,这样是不行的: 我们可以利用坐标的信息,将所有的点离散化后,以x优先按小到大排序,按y按大到小排序,这时维护一个DP(i) ,表示第I列的最值. j=0→i?1j=0→i?1                           d

CF809E Surprise me! 莫比乌斯反演、虚树、树形DP

传送门 简化题意:给出一棵\(n\)个点的树,编号为\(1\)到\(n\),第\(i\)个点的点权为\(a_i\),保证序列\(a_i\)是一个\(1\)到\(n\)的排列,求 \[ \frac{1}{n(n-1)} \sum\limits_{i=1}^n \sum\limits_{j=1}^n \varphi(a_ia_j) dist(i,j)\] 其中\(dist(i,j)\)为树上\(i,j\)两点的距离. 看到\(\varphi\)第一反应推式子 因为序列\(a_i\)是一个\(1\)到