[树的同构][二分][可并堆维护哈希] LOJ#6066 || BZOJ4928 && 2017 山东一轮集训 Day3. 第二题

来源:互联网 发布:数据结构 算法 c 编辑:程序博客网 时间:2024/06/04 18:16

这题一看就可以二分
那么解决这题的关键就变成了怎么对树进行哈希,以及怎么快速维护哈希值
想了一个下午想了一个比较靠谱的哈希方法。
用一个p进制数(p>n且为质数)来表示每一个节点,这个数有depth位,depth位这个节点的深度,那么这个数在p进制下第i位表示这个点的depth-i的祖先是其父亲的第几个儿子。
这里写图片描述

大概长这样
子树中所有节点的哈希值之和作为这个子树的哈希值。
因为每个哈希值的组合表示了唯一一颗子树,那么出现哈希值重合但是不同构的子树的概率就很小了

在二分中检验的时候,dfs一遍,可以用按深度建可并堆来维护K-子树,每次合并子树的可并堆,并在子树可并堆上打个乘p+tag的标记,并且把深度超过二分的d时弹出堆,然后用map记一下哈希值就可以了。

轻松垫底

#include <cstdio>#include <iostream>#include <algorithm>#include <vector>#include <map>#include <string>#include <cstring>using namespace std;const int N=100010,base=100003;typedef unsigned long long ll;int n,x,u,cnt;int depth[N];vector<int> son[N],l[N];map<ll,int> M;struct iheap{  iheap *l,*r;  ll val,mul,add,key; int deep,size;}h[N],*rt[N];inline char nc(){  static char buf[100000],*p1=buf,*p2=buf;  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline void rea(int &x){  char c=nc(); x=0;  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());}void dfs(int x){  for(int i=0;i<son[x].size();i++)    depth[son[x][i]]=depth[x]+1,dfs(son[x][i]);}inline void Mul(iheap *x,ll y){  if(!x) return ;  x->val*=y; x->mul*=y; x->add*=y; x->key*=y;}inline void Add(iheap *x,ll y){  if(!x) return ;  x->val+=y*x->size; x->add+=y; x->key+=y;}inline void Push(iheap *x){  if(x->mul!=1)    Mul(x->l,x->mul),Mul(x->r,x->mul),x->mul=1;  if(x->add)    Add(x->l,x->add),Add(x->r,x->add),x->add=0;}inline void Up(iheap *x){  x->size=(x->l?x->l->size:0)+(x->r?x->r->size:0)+1;  x->val=(x->l?x->l->val:0)+(x->r?x->r->val:0)+x->key;}inline int ran(){ static int x=31253125; x+=(x<<4)+1; return x&65536; }iheap *Merge(iheap *x,iheap *y){  if(!x||!y) return !x?y:x;  if(x->deep<y->deep) return Merge(y,x);  Push(x); Push(y);  if(ran()) x->l=Merge(x->l,y); else x->r=Merge(x->r,y);  return Up(x),x;}bool check(int x,int d){  for(int i=0;i<son[x].size();i++){    if(check(son[x][i],d)) return true;    Mul(rt[son[x][i]],base);    Add(rt[son[x][i]],i+1);    rt[x]=Merge(rt[x],rt[son[x][i]]);  }  h[++cnt].val=h[cnt].key=0; h[cnt].deep=depth[x];  h[cnt].mul=1; h[cnt].add=0; h[cnt].l=h[cnt].r=0; h[cnt].size=1;  rt[x]=Merge(rt[x],h+cnt);  while(rt[x] && rt[x]->deep>depth[x]+d)    Push(rt[x]),rt[x]=Merge(rt[x]->l,rt[x]->r);  if(!rt[x]||rt[x]->deep!=depth[x]+d) return false;  return M.count(rt[x]->val)?true:(M[rt[x]->val]=1,false);}inline void Clear(){  M.clear(); cnt=0;  for(int i=1;i<=n;i++) rt[i]=0;}int main(){  rea(n);  for(int i=1;i<=n;i++)    for(rea(x);x;x--)      rea(u),son[i].push_back(u);  dfs(1);  int L=1,R=n,mid,ans=0,sx=0;  while(L<=R) (Clear(),check(1,mid=L+R>>1))?L=(ans=mid)+1:R=mid-1;  printf("%d\n",ans);  return 0;}
阅读全文
0 0
原创粉丝点击