bzoj4754[JSOI2016]独特的树叶
这个题....别人写得怎么都....那么短啊?
我怎么....WA了好几次啊....怎么去loj扒了数据才调出来啊?
这个算法...怎么我还是不知道对不对啊
怎么回事啊怎么回事啊怎么回事啊?
请无视上面的梦话
代码太长应该是因为我把两棵树都有的函数通过复制改名实现,如果写成结构体大概能短一半吧
题意
给出两棵树A和B,A有n个点,B有n+1个点,且B是由A的某个点上多连一个点再把节点重新标号得到的.问多连的那个点在B中的编号.有多解时输出最小的.
分析
首先我们发现这是个树同构题目!然后我们只会用哈希做树同构!因此我们猜想一定可以快速求出树B删去某个点之后的哈希值!然后别人就会做了!我不会做!
考虑加一个点之后重心的变化.无根树重心有1到2个,如果有2个那么这两个一定相邻,且连接两个重心的边可以把树分成大小相等的两部分.那么我们分几种情况画画图,发现有这么几种情况:
- 加入节点之前有1个重心,加入后也是1个重心,那么这两个重心必然是同一个点.
- 加入节点之前有1个重心,加入后有2个重心,那么加入后的2个重心中必然有一个是原先的重心.
- 加入节点之前有2个重心,加入后有1个重心,那么加入后的1个重心必然是之前的2个重心当中的1个.
- 不存在加入之前有2个重心,加入后也有2个重心的情况,因为有2个重心要求树的节点个数必须是偶数.
那么不妨找出树A和B的重心,并求出以重心为根时所有子树的哈希值.因为加上的那个叶节点不可能成为重心,所以这个叶节点一定在以重心的某个儿子为根的子树内,而其他的子树必然是同构的,否则就不满足加上的节点只有1个.因此我们尝试把两棵树以重心为根的同构的子树匹配起来,如果最后只剩下两棵子树哈希值不同,就可以递归下去了.因为每次都是选的重心,所以递归深度不超过\(O(logn)\).
这里有两个细节:
- 重心可能有两个,这个时候要把两种可能的匹配方法都试一下,判断是否合法,如果都合法,就都递归下去.
- 向下递归时选出两棵哈希值不同的子树的方案有可能有多种,因为可能有同构的子树,选哪一个都可以.如果是树A的子树同构,选一个向下递归即可,因为在树A中我们要的只是一个树的形态用来和树B比较,选哪一棵无所谓.但是选择树B的不同子树向下递归可以得到不同的答案编号,所以如果树B选到的子树有同构的,每一棵都要递归下去试一试.
然后我们发现这个算法在loj上是20分,只过了两个小点
Life is like this.
原因是递归到树B中这样形态的子树时挂掉了:
1连2,2连3,3连4,5连3,5还连着其他一些点使得5是重心,发现多余的叶节点在1,2,3,4组成的子树内,然后选择1.但是实际上删去节点4才能和树A同构.原因是递归处理下去的时候我们只考虑了分割出的子树,忽略了和分割出的子树之外的点的关系...然后我们加一些特技...在树hash的时候考虑一下访问到的子树之外的点....作为特殊的参数和稀泥扔到树hash里....然后我们发现这样的忽略还会导致我们在有两个重心的时候选的重心产生"看似"合法的非法解,然后我们发现似乎只需要对每个点记录一下递归到这里时之前选取过的重心有多少个与之相邻,我们在两棵树中选取进行匹配的重心需要具有相同的这个参数...然后就能搞过去了....
实际上我也不知道这个算法有没有什么漏洞...有想法的同学欢迎造数据叉掉或者证明/证伪...把树hash叉掉的不算反正大家都写的树hash
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005;
struct edge{
int to,next;
}lst1[maxn<<1],lst2[maxn<<1];int len1=1,len2=1,first1[maxn],first2[maxn];
void addedge1(int a,int b){
lst1[len1].to=b;lst1[len1].next=first1[a];first1[a]=len1++;
}
void addedge2(int a,int b){
lst2[len2].to=b;lst2[len2].next=first2[a];first2[a]=len2++;
}
int totsz1;
int root1[3];
int sz1[maxn],f1[maxn];bool vis1[maxn];
void get_root1(int x,int p){
sz1[x]=1;f1[x]=0;
for(int pt=first1[x];pt;pt=lst1[pt].next){
if(lst1[pt].to==p||vis1[lst1[pt].to])continue;
get_root1(lst1[pt].to,x);
sz1[x]+=sz1[lst1[pt].to];
if(sz1[lst1[pt].to]>f1[x])f1[x]=sz1[lst1[pt].to];
}
if(totsz1-sz1[x]>f1[x])f1[x]=totsz1-sz1[x];
if(f1[x]<=totsz1/2)root1[++root1[0]]=x;
}
int totsz2;
int root2[3];
int sz2[maxn],f2[maxn];bool vis2[maxn];
void get_root2(int x,int p){
sz2[x]=1;f2[x]=0;
for(int pt=first2[x];pt;pt=lst2[pt].next){
if(lst2[pt].to==p||vis2[lst2[pt].to])continue;
get_root2(lst2[pt].to,x);
sz2[x]+=sz2[lst2[pt].to];
if(sz2[lst2[pt].to]>f2[x])f2[x]=sz2[lst2[pt].to];
}
if(totsz2-sz2[x]>f2[x])f2[x]=totsz2-sz2[x];
if(f2[x]<=totsz2/2)root2[++root2[0]]=x;
}
int ans=0x7f7f7f7f;
typedef unsigned long long ul;
ul H1[maxn],H2[maxn];
ul seq[maxn];
void gethash1(int x,int p){
H1[x]=233;sz1[x]=1;
for(int pt=first1[x];pt;pt=lst1[pt].next){
if(lst1[pt].to==p||vis1[lst1[pt].to])continue;
gethash1(lst1[pt].to,x);sz1[x]+=sz1[lst1[pt].to];
}
int cnt=0;
for(int pt=first1[x];pt;pt=lst1[pt].next){
if(lst1[pt].to==p)continue;
if(vis1[lst1[pt].to])seq[++cnt]=123123;
else seq[++cnt]=H1[lst1[pt].to];
}
sort(seq+1,seq+cnt+1);
for(int i=1;i<=cnt;++i){
H1[x]=(H1[x]*29+seq[i])^((seq[i]<<6)-H1[x])+23336666;
}
}
void gethash2(int x,int p){
H2[x]=233;sz2[x]=1;
for(int pt=first2[x];pt;pt=lst2[pt].next){
if(lst2[pt].to==p||vis2[lst2[pt].to])continue;
gethash2(lst2[pt].to,x);sz2[x]+=sz2[lst2[pt].to];
}
int cnt=0;
for(int pt=first2[x];pt;pt=lst2[pt].next){
if(lst2[pt].to==p)continue;
if(vis2[lst2[pt].to])seq[++cnt]=123123;
else seq[++cnt]=H2[lst2[pt].to];
}
sort(seq+1,seq+cnt+1);
for(int i=1;i<=cnt;++i){
H2[x]=(H2[x]*29+seq[i])^((seq[i]<<6)-H2[x])+23336666;
}
}
int seq1[maxn],cnt1,seq2[maxn],cnt2;
bool cmp1(const int &a,const int &b){
return H1[a]<H1[b];
}
bool cmp2(const int &a,const int &b){
return H2[a]<H2[b];
}
bool isleaf[maxn];
int beside1[maxn],beside2[maxn];
#define GG {for(int i=1;i<=cnt1;++i)beside1[seq1[i]]--;for(int i=1;i<=cnt2;++i)beside2[seq2[i]]--;return;}
void trymatch(int rt1,int rt2){
gethash1(rt1,0);
gethash2(rt2,0);
cnt1=cnt2=0;
for(int pt=first1[rt1];pt;pt=lst1[pt].next){
if(vis1[lst1[pt].to]){
continue;
}
beside1[lst1[pt].to]++;
seq1[++cnt1]=lst1[pt].to;
}
for(int pt=first2[rt2];pt;pt=lst2[pt].next){
if(vis2[lst2[pt].to]){
continue;
}
beside2[lst2[pt].to]++;
seq2[++cnt2]=lst2[pt].to;
}
if(cnt1>cnt2)GG;
if(cnt1<cnt2){
if(cnt1+1<cnt2)GG;
int leaf=0x7f7f7f7f;
for(int i=1;i<=cnt2;++i){
if(isleaf[seq2[i]]&&H2[seq2[i]]==233&&seq2[i]<leaf){
leaf=seq2[i];
}
}
if(leaf==0x7f7f7f7f)GG;
sort(seq1+1,seq1+cnt1+1,cmp1);
sort(seq2+1,seq2+cnt2+1,cmp2);
int pt=1;
for(int i=1;i<=cnt1;++i){
if(seq2[pt]==leaf)++pt;
if(H1[seq1[i]]!=H2[seq2[pt++]])GG;
}
ans=min(ans,leaf);
}else{
sort(seq1+1,seq1+cnt1+1,cmp1);
sort(seq2+1,seq2+cnt2+1,cmp2);
int pos2=0,cntdiff=0,pos1=0;
int pt=1;
for(int i=1;i<=cnt1;++i){
while(pt<cnt2&&H2[seq2[pt]]<H1[seq1[i]]){
pos2=pt;
++pt;
}
if(H2[seq2[pt]]!=H1[seq1[i]]){
pos1=i;
}
else cntdiff++,pt++;
}
if(pt==cnt2)pos2=cnt2;
if(cntdiff==cnt1-1){
ul sign=H2[seq2[pos2]];
totsz1=sz1[seq1[pos1]];totsz2=sz2[seq2[pos2]];
if(totsz1+1!=totsz2)GG;
int old1,old2;
old1=totsz1;old2=totsz2;
int l=1,r=cnt2;
while(H2[seq2[l]]!=sign)l++;
while(H2[seq2[r]]!=sign)r--;
for(int pos=l;pos<=r;++pos){
root1[0]=root2[0]=0;
totsz1=old1;totsz2=old2;
vis1[rt1]=true;vis2[rt2]=true;
get_root1(seq1[pos1],0);get_root2(seq2[pos],0);
int r1[3],r2[3];for(int i=0;i<3;++i)r1[i]=root1[i],r2[i]=root2[i];
for(int i=1;i<=r1[0];++i){
for(int j=1;j<=r2[0];++j){
if(beside1[r1[i]]==beside2[r2[j]])
trymatch(r1[i],r2[j]);
}
}
vis1[rt1]=false;vis2[rt2]=false;
}
}
}
GG;
}
int main(){
int n;scanf("%d",&n);
for(int i=1,a,b;i<n;++i){
scanf("%d%d",&a,&b);addedge1(a,b);addedge1(b,a);
}
for(int i=1,a,b;i<=n;++i){
scanf("%d%d",&a,&b);addedge2(a,b);addedge2(b,a);
}
for(int i=1;i<=n+1;++i){
int cnt=0;
for(int pt=first2[i];pt;pt=lst2[pt].next){
++cnt;
}
if(cnt==1)isleaf[i]=true;
}
totsz1=n;totsz2=n+1;root1[0]=root2[0]=0;
get_root1(1,0);get_root2(1,0);
int r1[3],r2[3];for(int i=0;i<3;++i)r1[i]=root1[i],r2[i]=root2[i];
for(int i=1;i<=r1[0];++i){
for(int j=1;j<=r2[0];++j){
trymatch(r1[i],r2[j]);
}
}
printf("%d\n",ans);
return 0;
}
bzoj4754[JSOI2016]独特的树叶的更多相关文章
- BZOJ4754 JSOI2016独特的树叶(哈希)
判断两棵无根树是否同构只需要把重心提作根哈希即可.由于只添加了一个叶子,重心的位置几乎不发生偏移,所以直接把两棵树的重心提起来,逐层找哈希值不同且对应的两子树即可.被一个普及组子问题卡一年. #inc ...
- 【BZOJ4754】独特的树叶(哈希)
[BZOJ4754]独特的树叶(哈希) 题面 BZOJ 给定一个\(n\)个节点的树A和一个\(n+1\)个节点的树\(B\) 求\(B\)的一个编号最小的节点,使得删去这个节点后\(A,B\)同构 ...
- BZOJ4754 & 洛谷4323 & LOJ2072:[JSOI2016]独特的树叶——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4754 https://www.luogu.org/problemnew/show/P4323 ht ...
- Luogu P4323 [JSOI2016]独特的树叶
一道比较好的树Hash的题目,提供一种不一样的Hash方法. 首先无根树的同构判断一般的做法只有树Hash,所以不会的同学可以做了Luogu P5043 [模板]树同构([BJOI2015]树的同构) ...
- BZOJ 4754 [JSOI2016]独特的树叶 | 树哈希判同构
题目链接 这道题是一道判断无根树同构的模板题,判断同构主要的思路就是哈希. 一遇到哈希题,一百个人能有一百零一种哈希方式,这篇题解随便选用了一种--类似杨弋<Hash在信息学竞赛中的一类应用&g ...
- Luogu 4323 [JSOI2016]独特的树叶
新技能get 树哈希,考虑到两棵树相同的条件,把每一个结点的哈希值和树的siz写进哈希值里去. 做出A树每一个结点为根时的树的哈希值丢进set中,然后暴力枚举B树中度数为1的点,求出删掉这个点之后的哈 ...
- P4323 [JSOI2016]独特的树叶(树哈希)
传送门 树哈希?->这里 反正大概就是乱搞--的吧-- //minamoto #include<bits/stdc++.h> #define R register #define l ...
- [JSOI2016]独特的树叶
https://zybuluo.com/ysner/note/1177340 题面 有一颗大小为\(n\)的树\(A\),现加上一个节点并打乱编号,形成树\(B\),询问加上的节点最后编号是多少? \ ...
- bzoj 4754: [Jsoi2016]独特的树叶
不得不说这是神题. %%% http://blog.csdn.net/samjia2000/article/details/51762811 #include <cstdio> #in ...
随机推荐
- 20155328 2016-2017-2 《Java程序设计》第二周学习总结
20155328 2006-2007-2 <Java程序设计>第2周学习总结 教材学习内容总结 基本类型: 整数:short整数(占2字节).int整数(占4字节).long整数(占8字节 ...
- mysql导入报错【The MySQL server is running with the --event-scheduler=DISABLED】
一.问题: 在进行mysql操作导入库的时候,报出了[The MySQL server is running with the --event-scheduler=DISABLED] 查看后台日志是事 ...
- PostgreSQL 使用 LDAP 认证方式
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL杂记页 回到顶级页面:PostgreSQL索引页 [作者 高健@博客园 luckyjackgao@gmail. ...
- 【转载】OGRE 2.1 Windows 编译
OGRE 2.1 Windows 编译 环境: Windows 7 64Bit Visual Studio 2012 OGRE 2.1 CMake 2.8.12.1 OGRE: OGRE官方推出了最新 ...
- day 8 list列表
列表 添加新的元素 append() 排队 insert() 插队 extend() 两队合成一队,狗尾续貂 删除元素 pop() ------> 删除最后一个 remove() ----> ...
- 3060 抓住那头奶牛 USACO
3060 抓住那头奶牛 USACO 时间限制: 1 s 空间限制: 16000 KB 题目等级 : 黄金 Gold 题目描述 Description 农夫约翰被告知一头逃跑奶牛的位置,想要立即抓住它, ...
- Nginx入门篇(一)之Nginx介绍
1.简介 Nginx ("engine x") 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 服务器. Nginx 是由 Igor Sysoe ...
- 三、Django安装和流程
一.MVC模式 MVC(Model-View-Controller),中文名“模型-视图-控制器”,是一个好的Web应用开发所遵循的模式,它有利于把Web应用的代码分解为易于管理的功能模块. M:Mo ...
- 一、初识 Django
一.引子 Django最初设计用于具有快速开发需求的新闻类站点,目的是要实现简单快捷的网站开发! 从好的方面来看,Web 开发激动人心且富于创造性:从另一面来看,它却是份繁琐而令人生厌的工作.通过减少 ...
- 转载-找圆算法((HoughCircles)总结与优化-霍夫变换
原文链接: http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096 找圆算法((HoughCircles)总结与优化 Ope ...