这个题....别人写得怎么都....那么短啊?

我怎么....WA了好几次啊....怎么去loj扒了数据才调出来啊?

这个算法...怎么我还是不知道对不对啊

怎么回事啊怎么回事啊怎么回事啊?

请无视上面的梦话

代码太长应该是因为我把两棵树都有的函数通过复制改名实现,如果写成结构体大概能短一半吧

题意

给出两棵树A和B,A有n个点,B有n+1个点,且B是由A的某个点上多连一个点再把节点重新标号得到的.问多连的那个点在B中的编号.有多解时输出最小的.

分析

首先我们发现这是个树同构题目!然后我们只会用哈希做树同构!因此我们猜想一定可以快速求出树B删去某个点之后的哈希值!然后别人就会做了!我不会做!

考虑加一个点之后重心的变化.无根树重心有1到2个,如果有2个那么这两个一定相邻,且连接两个重心的边可以把树分成大小相等的两部分.那么我们分几种情况画画图,发现有这么几种情况:

  1. 加入节点之前有1个重心,加入后也是1个重心,那么这两个重心必然是同一个点.
  2. 加入节点之前有1个重心,加入后有2个重心,那么加入后的2个重心中必然有一个是原先的重心.
  3. 加入节点之前有2个重心,加入后有1个重心,那么加入后的1个重心必然是之前的2个重心当中的1个.
  4. 不存在加入之前有2个重心,加入后也有2个重心的情况,因为有2个重心要求树的节点个数必须是偶数.

那么不妨找出树A和B的重心,并求出以重心为根时所有子树的哈希值.因为加上的那个叶节点不可能成为重心,所以这个叶节点一定在以重心的某个儿子为根的子树内,而其他的子树必然是同构的,否则就不满足加上的节点只有1个.因此我们尝试把两棵树以重心为根的同构的子树匹配起来,如果最后只剩下两棵子树哈希值不同,就可以递归下去了.因为每次都是选的重心,所以递归深度不超过\(O(logn)\).

这里有两个细节:

  1. 重心可能有两个,这个时候要把两种可能的匹配方法都试一下,判断是否合法,如果都合法,就都递归下去.
  2. 向下递归时选出两棵哈希值不同的子树的方案有可能有多种,因为可能有同构的子树,选哪一个都可以.如果是树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]独特的树叶的更多相关文章

  1. BZOJ4754 JSOI2016独特的树叶(哈希)

    判断两棵无根树是否同构只需要把重心提作根哈希即可.由于只添加了一个叶子,重心的位置几乎不发生偏移,所以直接把两棵树的重心提起来,逐层找哈希值不同且对应的两子树即可.被一个普及组子问题卡一年. #inc ...

  2. 【BZOJ4754】独特的树叶(哈希)

    [BZOJ4754]独特的树叶(哈希) 题面 BZOJ 给定一个\(n\)个节点的树A和一个\(n+1\)个节点的树\(B\) 求\(B\)的一个编号最小的节点,使得删去这个节点后\(A,B\)同构 ...

  3. BZOJ4754 & 洛谷4323 & LOJ2072:[JSOI2016]独特的树叶——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4754 https://www.luogu.org/problemnew/show/P4323 ht ...

  4. Luogu P4323 [JSOI2016]独特的树叶

    一道比较好的树Hash的题目,提供一种不一样的Hash方法. 首先无根树的同构判断一般的做法只有树Hash,所以不会的同学可以做了Luogu P5043 [模板]树同构([BJOI2015]树的同构) ...

  5. BZOJ 4754 [JSOI2016]独特的树叶 | 树哈希判同构

    题目链接 这道题是一道判断无根树同构的模板题,判断同构主要的思路就是哈希. 一遇到哈希题,一百个人能有一百零一种哈希方式,这篇题解随便选用了一种--类似杨弋<Hash在信息学竞赛中的一类应用&g ...

  6. Luogu 4323 [JSOI2016]独特的树叶

    新技能get 树哈希,考虑到两棵树相同的条件,把每一个结点的哈希值和树的siz写进哈希值里去. 做出A树每一个结点为根时的树的哈希值丢进set中,然后暴力枚举B树中度数为1的点,求出删掉这个点之后的哈 ...

  7. P4323 [JSOI2016]独特的树叶(树哈希)

    传送门 树哈希?->这里 反正大概就是乱搞--的吧-- //minamoto #include<bits/stdc++.h> #define R register #define l ...

  8. [JSOI2016]独特的树叶

    https://zybuluo.com/ysner/note/1177340 题面 有一颗大小为\(n\)的树\(A\),现加上一个节点并打乱编号,形成树\(B\),询问加上的节点最后编号是多少? \ ...

  9. bzoj 4754: [Jsoi2016]独特的树叶

    不得不说这是神题. %%%   http://blog.csdn.net/samjia2000/article/details/51762811 #include <cstdio> #in ...

随机推荐

  1. 20155328 2016-2017-2 《Java程序设计》第三周学习总结

    20155328 2016-2017-2 <Java程序设计>第三周学习总结 教材学习内容总结 类是对象的设计图,对象是类的实例.用class定义类,用new新建一个对象. 一个原始码中可 ...

  2. su的使用与退出

    偶尔用回到ubuntu系统,想切换到su,总是显示不成功,也许是初次使用,即需要设定一下: 使用sudo $:sudo passwd 系统提示输入密码,即安装时的用户密码,然后,系统提示输入两次新密码 ...

  3. ASCII, UNICODE, UTF-8, 字符集理解

    字符编码的发展历史 一个字节:最初一个字节的标准是混乱的,出现过4位.6位.7位的一字节标准,最终由于历史原因和物理存储需求(8位是2的3次方,方便物理存储),所以采用了8位为一个字节的标准. ASC ...

  4. 简单的Slony-I设置实例

    磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL集群方案相关索引页     回到顶级页面:PostgreSQL索引页 参考如下链接: http://lets.postg ...

  5. day4 CSS属性操作

    1.CSS属性 基本属性 height, 高度 百分比 width, 宽度 像素,百分比 text-align:ceter, 水平方向居中 line-height, 垂直方向根据标签高度 color. ...

  6. java 文件过滤

    public class TestFileio { public static void main(String[] args) { File file = new File("D:/upl ...

  7. 在BAE上部署Pomelo

    BAE升级到3.0后顿时感觉好用了很多,俨然云主机的感觉. 底下我将分享我在BAE上部署Pomelo的过程. 首先需要拥有一个BAE的执行单元.没有的可以自行百度并部署. 接着svn得出代码到本地.此 ...

  8. 九、Django之ORM

    一.ORM概述 用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作. 到目前为止,当我们的程序涉及到数据库相关操作时,一般操作流 ...

  9. vue cli 3 +jquery

    const webpack = require('webpack')module.exports = { // baseUrl type:{string} default:'/' // 将部署应用程序 ...

  10. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift

    1. 摘要 训练深层的神经网络非常困难,因为在训练的过程中,随着前面层数参数的改变,每层输入的分布也会随之改变.这需要我们设置较小的学习率并且谨慎地对参数进行初始化,因此训练过程比较缓慢. 作者将这种 ...