关于仙人掌的同构,主要是我太蒟蒻了QAQ,问了好几位大佬才弄好。

手撕仙人掌,你得先有手套 ,你得先了解以下基本知识

a.点双连通分量,没什么好说得,仙人掌上有环,判环用点双

b.树的hash点这里

c.仙人掌点这里                              

对于一棵仙人掌,我们通过一些方法来简化:                 

我们最讨厌的是环,假如说没有环,那么树的hash还是蛮简单的。          

OK那么就是圆方树了,如果你还不知道什么是圆方树,请自行百度或者点这里。

当然对于判定仙人掌的同构,不需要一颗完整的圆方树,只需要在环中间建点

单纯地表示一个环,使用最小表示法(这部分内容我稍后补充QAQ)

单纯地表示一棵树,hash。

嗯,如果你觉得我上面说得十分模糊,那么是正确的,因为还没有开始呢  ^_^

(我其实是不会告诉你我没有用tarjan和完整的圆方树的)

e.g.一个只有一个环的仙人掌

假设我们是这样搜索的

那么在dfs的过程中,要判定一个点在不在环上,记录它的父节点这样在回溯到4时,发现他有一个不是自己儿子的子节点(9)

哈,那就是环的另一端了。

这时抓出9,一直回溯父亲直到4被枚举到,整个环就被揪了出来。

新建一个节点,(在圆方树里则称方点)依次连边。

而对于非环上边直接连就好了

像这样就建立了一个独立出原图的树(一位大佬是这样描述的)

多么优雅的一棵树

好了,现在它是无根的找一下树的重心

新建一个根节点hash一下就好了^_^

你真的觉得这就完了

你太天真了

环是不能这样子搞的

正确打开方式:

别忘了真正的环上点是有顺序的

假如说这个环的方点(中间那个新建点)是根,跑最小表示法(然而我代码里没有这一项QAQ我改天加上)

对于一个普通树节点比如说这样

这样就可以了

代码实现

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ult;
const ult seed1=1324983271ull;
const ult seed2=4327894239ull;
const ult lth1=9301248721ull;
const ult lth2=8317498371ull;
int nt,mt;
bool cmp(ult x,ult y)
{
return x<y;
}
struct pnt{
int hd;
int sh;
int fa;
int wgt;
bool ong;
bool vis;
bool chkd;
ult has;
};
struct ent{
int twd;
int lst;
};
struct Cactus{
pnt p[];
ent e[];
ent r[];
ult st[];
int rt[];
int n,m;
int sqn;
int cnt;
int cmt;
void e_ade(int f,int t)
{
cnt++;
e[cnt].twd=t;
e[cnt].lst=p[f].hd;
p[f].hd=cnt;
}
void r_ade(int f,int t)
{
cmt++;
r[cmt].twd=t;
r[cmt].lst=p[f].sh;
p[f].sh=cmt;
}
void tr_dfs(int x,int f)
{
p[x].vis=true;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if((i^f)==)continue;
if(!p[to].vis)
{
p[to].fa=x;
p[x].ong=false;
tr_dfs(to,i);
if(!p[x].ong)
{
r_ade(x,to);
r_ade(to,x);
}
}else{
if(p[to].chkd)continue;
sqn++;
int u=x;
for(u=x;;u=p[u].fa)
{
r_ade(n+sqn,u);
r_ade(u,n+sqn);
p[u].ong=true;
if(u==to)break;
}
}
}
p[x].chkd=true;
}
void gravity(int x,int f)
{
p[x].wgt=;
bool fl=true;
for(int i=p[x].sh;i;i=r[i].lst)
{
int to=r[i].twd;
if(to==f)continue;
gravity(to,x);
p[x].wgt+=p[to].wgt;
if(p[to].wgt*>sqn+n)
fl=false;
}
if((sqn+n-p[x].wgt)*>sqn+n)
fl=false;
if(fl)
{
if(rt[])
{
rt[]=x;
}else{
rt[]=x;
}
}
}
void Hash(int x,int f)
{
int top=;
for(int i=p[x].sh;i;i=r[i].lst)
{
int to=r[i].twd;
if(to==f)continue;
Hash(to,x);
}
if(x<=n)
{
for(int i=p[x].sh;i;i=r[i].lst)
{
int to=r[i].twd;
if(to==f)continue;
st[++top]=p[to].has;
}
sort(st+,st+top+,cmp);
p[x].has=seed1;
for(int i=;i<=top;i++)
{
p[x].has=((p[x].has*lth1+st[i])^st[i])+st[i];
}
}else{
int i;
for(i=p[x].sh;i;i=r[i].lst)
{
if(r[i].twd==f)
break;
}
for(i=r[i].lst;i;i=r[i].lst)
{
int to=r[i].twd;
st[++top]=p[to].has;
}
for(i=p[x].sh;i;i=r[i].lst)
{
int to=r[i].twd;
if(to==f)
break;
st[++top]=p[to].has;
}
ult tmp1=seed2,tmp2=seed2;
for(i=;i<=top;i++)
{
tmp1=((tmp1*lth2+st[i])^st[i])+st[i];
}
for(i=top;i;i--)
{
tmp2=((tmp2*lth2+st[i])^st[i])+st[i];
}
p[x].has=min(tmp1,tmp2);
p[x].has*=lth2;
}
}
ult solve(int nn,int mm)
{
n=nn;
m=mm;
cnt=;
for(int i=;i<=m;i++)
{
int x;
int y;
scanf("%d%d",&x,&y);
e_ade(x,y);
e_ade(y,x);
}
tr_dfs(,);
gravity(,);
r_ade(,rt[]);
if(rt[])
{
r_ade(,rt[]);
for(int i=p[rt[]].sh;i;i=r[i].lst)
{
if(r[i].twd==rt[])
r[i].twd=;
}
for(int i=p[rt[]].sh;i;i=r[i].lst)
{
if(r[i].twd==rt[])
r[i].twd=;
}
}
Hash(,);
return p[].has;
}
}C[];
int main()
{
scanf("%d%d",&nt,&mt);
if(C[].solve(nt,mt)==C[].solve(nt,mt))
{
printf("YES\n");
}else{
printf("NO\n");
}
return ;
}

大概就是这样了^_^

仙人掌的同构(hash)的更多相关文章

  1. BZOJ4337: BJOI2015 树的同构(hash 树同构)

    题意 题目链接 Sol 树的同构问题,直接拿hash判一下,具体流程大概是这样的: 首先转化为有根树,预处理出第\(i\)棵树以\(j\)为根时的hash值. 那么两个树同构当且仅当把两棵树的hash ...

  2. luogu P5043 【模板】树同构 hash 最小表示法

    LINK:模板 树同构 题目说的很迷 给了一棵有根树 但是重新标号 言外之意还是一棵无根树 然后要求判断是否重构. 由于时无根的 所以一个比较显然的想法暴力枚举根. 然后做树hash或者树的最小表示法 ...

  3. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  4. bzoj3871: [Neerc2013 C]Cactus Automorphisms || 3899: 仙人掌树的同构

    Description 给定一个N,N<=50 000个节点的仙人掌,其是指每条边最多在一个环中的无向图,求仙人掌有多少种自同构.自同构是指得是图的顶点集合V到V的变换M, 以P1^a1*P2^ ...

  5. BZOJ 4337: BJOI2015 树的同构 树hash

    4337: BJOI2015 树的同构 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4337 Description 树是一种很常见的数 ...

  6. 【NOI2013模拟】坑带的树(仙人球的同构+圆方树乱搞+计数+HASH)

    [NOI2013模拟]坑带的树 题意: 求\(n\)个点,\(m\)条边的同构仙人球个数. \(n\le 1000\) 这是一道怎么看怎么不可做的题. 这种题,肯定是圆方树啦~ 好,那么首先转为广义圆 ...

  7. BZOJ3899 仙人掌树的同构(圆方树+哈希)

    考虑建出圆方树.显然只有同一个点相连的某些子树同构会产生贡献.以重心为根后(若有两个任取一个即可),就只需要处理子树内部了. 如果子树的根是圆点,其相连的同构子树可以任意交换,方案数乘上同构子树数量的 ...

  8. BZOJ4337:[BJOI2015]树的同构(树hash)

    Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如 ...

  9. 刷题总结——树的同构(bzoj4337 树上hash)

    Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如 ...

随机推荐

  1. python之文件操作-复制、剪切、删除等

    以下是把sourceDir目录下的以.JPG结尾的文件所有拷贝到targetDir目录下: <span style="font-size:18px;">>> ...

  2. CentOS用rpm升级glibc

    CentOS用rpm升级glibc #! /bin/sh # update glibc to 2.23 for CentOS 6 wget http://cbs.centos.org/kojifile ...

  3. POJ 1671 第二类斯特林数

    思路: 递推出来斯特林数 求个和 if(i==j)f[i][j]=1; else f[i][j]=f[i-1][j-1]+f[i-1][j]*j; //By SiriusRen #include &l ...

  4. 超级硬件代理解决企业Web提速上网问题

    超级硬件代理解决企业Web提速上网问题 需求分析: XX集团是五家企业重组建立的特大型工程勘察设计咨询企业,下设10多个分公司,上网人数众多.有多台WEB server,对外服务,访问量及大.以前无论 ...

  5. java线程深入学习

    一.java中的线程是通过Thread类创建的, //下面是构造函数,一个共同的特点就是:都是调用init()进行创建的 public Thread() { init(null, null, &quo ...

  6. OpenSUSE42.3 leap 软件源设置

    一.OpenSUSE软件源介绍: 1.默认已经加入了官方的软件源,不过我们自己也可以根据需要添加很多非官方软件源,添加软件源时要注意: 非官方源可能包含一些试验中的不稳定的软件包    不同的软件源之 ...

  7. 记一次 Apache HUE 优化之因使用 Python 魔术方法而遇到的坑

    最近的工作是基于 Apache HUE 做二次开发.刚接手 HUE 的代码的时候,内心是崩溃的:开源的代码,风格很多种, 代码比较杂乱; 虽是基于 Django 开发的,但是项目的结构改变很大; 很多 ...

  8. JAVA工程命名规范

    Java推荐的包声明命名约定是反向域名. 例如 - com.abysm.myproject

  9. CSUOJ 1551 Longest Increasing Subsequence Again

    1551: Longest Increasing Subsequence Again Time Limit: 2 Sec  Memory Limit: 256 MBSubmit: 75  Solved ...

  10. 1.Maven之(一)Maven是什么

    转自:https://blog.csdn.net/xhxmister/article/details/79409208 首先,Maven的正确发音是[ˈmevən],而不是“马瘟”以及其他什么瘟.Ma ...