考虑建出圆方树。显然只有同一个点相连的某些子树同构会产生贡献。以重心为根后(若有两个任取一个即可),就只需要处理子树内部了。

  如果子树的根是圆点,其相连的同构子树可以任意交换,方案数乘上同构子树数量的阶乘即可。而若是方点,注意到其相邻的圆点在原树中是有序地在一个环上的,要产生同构只能旋转或翻转该环。并且因为一开始我们选择了重心为根,所以对于非重心的方点,将其所在的环旋转显然是无法产生贡献的。所以对于方点的所有孩子按环上顺序存储,其哈希值应以该顺序计算,正反取较小的,算贡献时对非重心点只考虑翻转,重心特判一下。判同构当然采取哈希。

  调了一年发现只有点双写错了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define N 2010
#define P 1000000003
#define M 10010
#define ull unsigned long long
#define p1 509
#define p2 923
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,p[N],t,dfn[N],low[N],stk[N],top,cnt,tot,ans=;
vector<int> BCC[N];
struct data{int to,nxt;
}edge[M];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void tarjan(int k)
{
dfn[k]=low[k]=++cnt;
stk[++top]=k;
for (int i=p[k];i;i=edge[i].nxt)
if (dfn[edge[i].to]) low[k]=min(low[k],dfn[edge[i].to]);
else
{
tarjan(edge[i].to);
low[k]=min(low[k],low[edge[i].to]);
if (low[edge[i].to]>=dfn[k])
{
tot++;
while (stk[top]!=edge[i].to) BCC[tot].push_back(stk[top]),top--;
BCC[tot].push_back(edge[i].to);top--;
BCC[tot].push_back(k);
}
}
}
namespace blocktree
{
int p[N],t,size[N];
ull hash[N],a[N];
struct data{int to,nxt;}edge[M];
void addedge(int x,int y)
{
//cout<<x<<' '<<y<<endl;
t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;
t++;edge[t].to=x,edge[t].nxt=p[y],p[y]=t;
}
void make(int k,int from)
{
size[k]=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from)
{
make(edge[i].to,k);
size[k]+=size[edge[i].to];
}
}
int findroot(int k,int from,int s)
{
int mx=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&size[edge[i].to]>size[mx]) mx=edge[i].to;
if ((size[mx]<<)>s) return findroot(mx,k,s);
else return k;
}
void dfs(int k,int from)
{
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) dfs(edge[i].to,k);
if (k<=n)
{
int cnt=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) a[++cnt]=hash[edge[i].to];
sort(a+,a+cnt+);
for (int i=;i<=cnt;i++) hash[k]=hash[k]*p1+a[i];
hash[k]=hash[k]*p2+size[k];
for (int i=;i<=cnt;i++)
{
int t=i;
while (t<cnt&&a[t+]==a[i]) t++;
int fac=;
for (int j=;j<=t-i+;j++) fac=1ll*fac*j%P;
ans=1ll*ans*fac%P;
i=t;
}
}
else if (k!=from)
{
if (BCC[k-n].size()>)
{
int pos;
for (int i=;i<BCC[k-n].size();i++)
if (BCC[k-n][i]==from) {pos=i;break;}
ull hash1=,hash2=;int x=pos,y=pos;
for (int i=;i<BCC[k-n].size();i++)
{
x--;if (x<) x+=BCC[k-n].size();
hash1=hash1*p1+hash[BCC[k-n][x]];
y++;if (y==BCC[k-n].size()) y=;
hash2=hash2*p1+hash[BCC[k-n][y]];
}
if (hash1==hash2) ans=2ll*ans%P;
hash[k]=min(hash1,hash2)*p2+size[k];
}
else for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) hash[k]=hash[edge[i].to]*p2+size[k];
}
else
{
ull hash1=;
for (int i=;i<BCC[k-n].size();i++)
hash1=hash1*p1+hash[BCC[k-n][i]];
int cnt=;
for (int i=;i<BCC[k-n].size();i++)
{
int x=i,y=i;ull h1=,h2=;
for (int j=;j<BCC[k-n].size();j++)
{
h1=h1*p1+hash[BCC[k-n][x]];
h2=h2*p1+hash[BCC[k-n][y]];
x++;if (x==BCC[k-n].size()) x=;
y--;if (y<) y+=BCC[k-n].size();
}
if (h1==hash1) cnt++;
if (h2==hash1) cnt++;
}
if (BCC[k-n].size()==) cnt/=;
ans=1ll*ans*cnt%P;
}
}
void solve()
{
make(,);
int root=findroot(,,size[]);
make(root,root);
dfs(root,root);
//cout<<root<<endl;
//for (int i=1;i<=19;i++) cout<<size[i]<<' '<<hash[i]<<endl;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj3899.in","r",stdin);
freopen("bzoj3899.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
for (int i=;i<=m;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
tarjan();
for (int i=;i<=tot;i++)
for (int j=;j<BCC[i].size();j++)
blocktree::addedge(n+i,BCC[i][j]);
blocktree::solve();
cout<<ans;
return ;
}

BZOJ3899 仙人掌树的同构(圆方树+哈希)的更多相关文章

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

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

  2. UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ23.html 题目传送门 - UOJ#23 题意 给定一个有 n 个节点的仙人掌(可能有重边). 对于所有 ...

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

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

  4. 仙人掌 && 圆方树 && 虚树 总结

    仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...

  5. UOJ.87.mx的仙人掌(圆方树 虚树)(未AC)

    题目链接 本代码10分(感觉速度还行..). 建圆方树,预处理一些东西.对询问建虚树. 对于虚树上的圆点直接做:对于方点特判,枚举其所有儿子,如果子节点不在该方点代表的环中,跳到那个点并更新其val, ...

  6. 圆方树总结 [uoj30]Tourists

    圆方树总结 所谓圆方树就是把一张图变成一棵树. 怎么变啊qaq 这里盗一张图 简单来说就是给每一个点双新建一个点,然后连向这个点双中的每一个点.特殊的,把两个点互相连通的也视作一个点双. 我们把原来就 ...

  7. [JZOJ 5909] [NOIP2018模拟10.16] 跑商(paoshang) 解题报告 (圆方树)

    题目链接: https://jzoj.net/senior/#contest/show/2529/2 题目: 题目背景:尊者神高达很穷,所以他需要跑商来赚钱题目描述:基三的地图可以看做 n 个城市,m ...

  8. 【题解】Uoj#30 Tourist(广义圆方树+树上全家桶)

    [题解]Uoj#30 Tourist(广义圆方树+树上全家桶) 名字听起来很霸气其实算法很简单.... 仙人掌上的普通圆方树是普及题,但是广义圆方树虽然很直观但是有很多地方值得深思 说一下算法的流程: ...

  9. Note -「圆方树」学习笔记

    目录 圆方树的定义 圆方树的构造 实现 细节 圆方树的运用 「BZOJ 3331」压力 「洛谷 P4320」道路相遇 「APIO 2018」「洛谷 P4630」铁人两项 「CF 487E」Touris ...

  10. Codeforces 487E Tourists [广义圆方树,树链剖分,线段树]

    洛谷 Codeforces 思路 首先要莫名其妙地想到圆方树. 建起圆方树后,令方点的权值是双联通分量中的最小值,那么\((u,v)\)的答案就是路径\((u,v)\)上的最小值. 然而这题还有修改, ...

随机推荐

  1. java.lang.UnsatisfiedLinkError: No implementation found for int com.baidu.platform.comjni.map.commonmemcache.JNICommonMemCache.Create()

    完整异常: Process: com.example.ai.tabhostdemo, PID: 1287 java.lang.UnsatisfiedLinkError: No implementati ...

  2. 异步方法(promise版)出错自调用

    /** * [*promisePlus promise封装的异步,既然是异步,必然会成功或者失败,理论上失败了就失败了,但是 * 失败后能否让他过多长时间自动再调用自己呢,如果调用指定的次数还是失败, ...

  3. 阅读Cortex-A53 Technical Reference Manual笔记

    1. 前言 一颗芯片最主要的就是CPU核了,处理CPU Core之外,还存在很多其他IP,包括Graphical.Multimedia.Memory Controller.USB Controller ...

  4. Luogu3162 CQOI2012 组装 贪心

    传送门 如果提供每一种零件的生产车间固定了,那么总时间\(t\)与组装车间的位置\(x\)的关系就是 \(t = \sum (x-a_i)^2 = nx^2-2\sum a_ix + \sum a_i ...

  5. JS-JS作用域问题

    一. js没有块级作用域(可以自己闭包或其他方法实现),只有函数级作用域,函数外面的变量函数里面可以找到,函数里面的变量外面找不到. var a=10; function fn(){ console. ...

  6. Spring Boot Admin 日志查看功能

    按照官方配置POM和配置文件后,能够结合Eureka查看各微服务状态,但是日志始终查看不了,出现406等错误. 最后偶然发现,是在在从官方网站拷贝配置的时候,出现的问题. logging.file=* ...

  7. 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案)

    主要功能:变灰,去噪,等提高清晰度等 代码类博客,无需多说,如下: public class UnCodebase { public Bitmap bmpobj; public UnCodebase( ...

  8. 普通程序员看k8s基于角色的访问控制(RBAC)

    一.知识准备 ● 上一节描述了k8s的账户管理,本文描述基于角色的访问控制 ● 网上RBAC的文章非常多,具体概念大神们也解释得很详细,本文没有站在高屋建瓴的角度去描述RBAC,而是站在一个普通程序员 ...

  9. zabbix监控主机cpu达到80%后报警

    在zabbix监控中,默认cpu监控模板中的触发器,当负载在一定时间内(比如最近5分钟)超过5以上为报警阀值.但是在实际场景中,由于服务器配置不一样,这个默认的cpu触发器用起来意义就不大了,这时候就 ...

  10. css3 动画效果实现

    前沿 在工作中,经常有一些需要切换的交互样式.如果直接在两种状态之间切换,就显得有点生硬.加上一些动画效果就会好很多. 示例1:点击的三角切换 实现过程 第一步实现这个三角形 用的svg 的多边形画法 ...