[树的同构][二分][可并堆维护哈希] 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
- [树的同构][二分][可并堆维护哈希] LOJ#6066 || BZOJ4928 && 2017 山东一轮集训 Day3. 第二题
- [计数][容斥] LOJ#6065 || BZOJ4927 && 2017 山东一轮集训 Day3. 第一题
- [线段树][二分图 霍尔定理]LOJ#6062 && 2017 山东一轮集训 Day2. Pair
- [莫队维护DP] LOJ#6074. 「2017 山东一轮集训 Day6」子序列
- [LOJ#6060][线性基]2017 山东一轮集训 Day1. Set
- loj #6062. 「2017 山东一轮集训 Day2」Pair(线段树)
- LOJ #6077. 「2017 山东一轮集训 Day7」逆序对
- [DP][倍增NTT]LOJ#6059. 2017 山东一轮集训 Day1. Sum
- LOJ #6077. 「2017 山东一轮集训 Day7」逆序对
- [霍尔定理]「2017 山东一轮集训 Day2」LOJ 6062——PAIR
- [倍增NTT][DP] LOJ#6059. 「2017 山东一轮集训 Day1」Sum
- [动态网络 网络流] LOJ#6068.「2017 山东一轮集训 Day4」棋盘
- [最短路 杂题] LOJ#6075. 「2017 山东一轮集训 Day6」重建
- [费用流]LOJ#6079. 「2017 山东一轮集训 Day7」养猫
- [后缀自动机 DP] LOJ#6071. 「2017 山东一轮集训 Day5」字符串
- [分块 回文自动机] LOJ#6070. 「2017 山东一轮集训 Day4」基因
- [容斥 DP] LOJ#6077. 「2017 山东一轮集训 Day7」逆序对
- LOJ 6100 「2017 山东二轮集训 Day1」第一题
- MTK电话本联系人备份加密与破解
- xml文件的解析
- mysql关于时间查询 : 获取2017-06-14 17:33:03到现在的所有订单
- IOS Monkey测试1
- 二叉树代码实现与详解--(C++版)
- [树的同构][二分][可并堆维护哈希] LOJ#6066 || BZOJ4928 && 2017 山东一轮集训 Day3. 第二题
- HtmlAgilityPack之HtmlNode类
- Spring Web MVC处理请求的流程
- 限制EditText只输入数字和字母
- javascript Date format(js日期格式化)
- 【二叉树】计算左叶的和
- 在xml中的一些特殊符号的使用如:> < &等等
- nagios监控mysql(percona-monitoring-plugins)
- Request库