BZOJ3899 仙人掌树的同构(圆方树+哈希)
考虑建出圆方树。显然只有同一个点相连的某些子树同构会产生贡献。以重心为根后(若有两个任取一个即可),就只需要处理子树内部了。
如果子树的根是圆点,其相连的同构子树可以任意交换,方案数乘上同构子树数量的阶乘即可。而若是方点,注意到其相邻的圆点在原树中是有序地在一个环上的,要产生同构只能旋转或翻转该环。并且因为一开始我们选择了重心为根,所以对于非重心的方点,将其所在的环旋转显然是无法产生贡献的。所以对于方点的所有孩子按环上顺序存储,其哈希值应以该顺序计算,正反取较小的,算贡献时对非重心点只考虑翻转,重心特判一下。判同构当然采取哈希。
调了一年发现只有点双写错了。
#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 仙人掌树的同构(圆方树+哈希)的更多相关文章
- 【NOI2013模拟】坑带的树(仙人球的同构+圆方树乱搞+计数+HASH)
[NOI2013模拟]坑带的树 题意: 求\(n\)个点,\(m\)条边的同构仙人球个数. \(n\le 1000\) 这是一道怎么看怎么不可做的题. 这种题,肯定是圆方树啦~ 好,那么首先转为广义圆 ...
- UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ23.html 题目传送门 - UOJ#23 题意 给定一个有 n 个节点的仙人掌(可能有重边). 对于所有 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- 仙人掌 && 圆方树 && 虚树 总结
仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...
- UOJ.87.mx的仙人掌(圆方树 虚树)(未AC)
题目链接 本代码10分(感觉速度还行..). 建圆方树,预处理一些东西.对询问建虚树. 对于虚树上的圆点直接做:对于方点特判,枚举其所有儿子,如果子节点不在该方点代表的环中,跳到那个点并更新其val, ...
- 圆方树总结 [uoj30]Tourists
圆方树总结 所谓圆方树就是把一张图变成一棵树. 怎么变啊qaq 这里盗一张图 简单来说就是给每一个点双新建一个点,然后连向这个点双中的每一个点.特殊的,把两个点互相连通的也视作一个点双. 我们把原来就 ...
- [JZOJ 5909] [NOIP2018模拟10.16] 跑商(paoshang) 解题报告 (圆方树)
题目链接: https://jzoj.net/senior/#contest/show/2529/2 题目: 题目背景:尊者神高达很穷,所以他需要跑商来赚钱题目描述:基三的地图可以看做 n 个城市,m ...
- 【题解】Uoj#30 Tourist(广义圆方树+树上全家桶)
[题解]Uoj#30 Tourist(广义圆方树+树上全家桶) 名字听起来很霸气其实算法很简单.... 仙人掌上的普通圆方树是普及题,但是广义圆方树虽然很直观但是有很多地方值得深思 说一下算法的流程: ...
- Note -「圆方树」学习笔记
目录 圆方树的定义 圆方树的构造 实现 细节 圆方树的运用 「BZOJ 3331」压力 「洛谷 P4320」道路相遇 「APIO 2018」「洛谷 P4630」铁人两项 「CF 487E」Touris ...
- Codeforces 487E Tourists [广义圆方树,树链剖分,线段树]
洛谷 Codeforces 思路 首先要莫名其妙地想到圆方树. 建起圆方树后,令方点的权值是双联通分量中的最小值,那么\((u,v)\)的答案就是路径\((u,v)\)上的最小值. 然而这题还有修改, ...
随机推荐
- IDEA注册jar包使用和常用插件
IDEA注册jar包使用 点击获取下载地址或生成注册码 一.安装完成后,先不启动,首先如下图修改相关的地方. 二.启动IDEA,并且激活IDEA IDEA插件仓库 IntelliJ IDEA Plug ...
- HDMI接口的PCB设计
1.定义 HDMI的全称是“HighDefinitionMultimedia”,即:高清多媒体接口. HDMI在引脚上和DVI兼容,只是采用了不同的封装.与DVI相比.HDMI可以传输数字音频信号,并 ...
- Intellij实用技巧
快捷键 Tradition 快捷键 介绍 Ctrl + Z 撤销 Ctrl + Shift + Z 取消撤销 Ctrl + X 剪切 Ctrl + C 复制 Ctrl + S 保存 Tab 缩进 Sh ...
- 如何使用chrome浏览器进行js调试找出元素绑定的点击事件
大家有没有遇到这样的一个问题,我们在分析一些大型电子商务平台的Web前端脚本时,想找到一个元素绑定的点击事件,并不是那么容易,因为有些前端脚本封装的比较隐蔽,甚至有些加密脚本,用传统的查找元素ID.或 ...
- GeForce Experience关闭自动更新
GeForce Experience驱动更新很烦,而且有时更新后就打不开了,找到种方法关闭更新 1.安装并登陆 2.打开 C:\ProgramData\NVIDIA Corporation 3.进入D ...
- 总结几个常用的系统安全设置(含DenyHosts)
1)禁止系统响应任何从外部/内部来的ping请求攻击者一般首先通过ping命令检测此主机或者IP是否处于活动状态如果能够ping通 某个主机或者IP,那么攻击者就认为此系统处于活动状态,继而进行攻击或 ...
- linux上启动tomcat远程不能访问
linux上关闭防火墙同样访问不了,执行iptables -f即可. 你试一试这个“iptables -F”然后再访问,如果能够访问了,那么需要执行“firewall-cmd --add-port=8 ...
- 【Beta阶段】第十次Scrum Meeting!!!
每日任务内容: 本次会议为第十次Scrum Meeting会议~ 本次会议为团队Beta阶段的最后一次会议!! 队员 今日完成任务 刘乾 #136(完成一半,今晨发布) 团队博客撰写 https:// ...
- 使用highcharts绘制美观的燃尽图
使用highcharts绘制美观的燃尽图 助教在博客中介绍了两种绘制燃尽图的方法,但是我们组在使用时发现有些任务不适合写进issue,而且网站生成的燃尽图不是很美观,因此我们打算使用其他方法自己绘制燃 ...
- 《Linux内核分析》第七周学习总结
<Linux内核分析>第七周学习总结 ——可执行程序的装载 姓名:王玮怡 学号:20135116 一.理论部分总结 (一)可执行程序的装载 ...