Description

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

以P1^a1*P2^a2...Pk^ak的形式输出,其中Pk是素数。

建圆方树,找到重心,如果重心有两个就在它们之间的边上插一个点,同构变换后重心不变

以重心为根得到有根树,对于树点,统计交换子树造成的同构,将所有贡献累乘得到答案,对于环点,如果不是重心,就只有翻转这个环能产生新的同构,否则这个环除了翻转还可以旋转,用hash处理这些情况。注意处理环的时候需要把环点的相邻点排成原来在仙人掌上的顺序。

#include<cstdio>
#include<algorithm>
typedef unsigned long long u64;
const int N=;
int n,m,fac[N];
int es[N],enx[N],e0[N],e1[N],ep=;
bool _c[N];
void ae(int*e,int a,int b){
es[ep]=b;enx[ep]=e[a];e[a]=ep++;
es[ep]=a;enx[ep]=e[b];e[b]=ep++;
}
void de(int*e,int a,int b){
for(int*i=e+a;*i;i=enx+*i){
int u=es[*i];
if(u==b){
*i=enx[*i];
return;
}
}
}
int dfn[N],low[N],tk=,ss[N],sp=,n1;
void tj(int w,int pa){
dfn[w]=low[w]=++tk;
ss[++sp]=w;
for(int i=e0[w];i;i=enx[i]){
int u=es[i];
if(!u)continue;
if(!dfn[u]){
es[i^]=;
tj(u,w);
es[i^]=w;
if(ss[sp]==u)--sp,ae(e1,w,u);
}else if(dfn[u]<dfn[w]){
int rp=;
ae(e1,u,++n1);
_c[n1]=;
while(sp&&dfn[ss[sp]]>dfn[u])ae(e1,ss[sp--],n1);
}
}
}
int sz[N],cg[],cgp=,rt;
void f1(int w,int pa){
bool is=;
sz[w]=;
for(int i=e1[w];i;i=enx[i]){
int u=es[i];
if(u==pa)continue;
f1(u,w);
sz[w]+=sz[u];
if(sz[u]*>n1)is=;
}
if(sz[w]*<n1)is=;
if(is)cg[cgp++]=w;
}
u64 h[N],h1[N],h2[N];
int cs[N],cp;
bool cmp_h(int a,int b){return h[a]<h[b];}
int a1[N],a2[N],mx=;
u64 a3[N];
void mul(int x){
++a1[x];
if(x>mx)mx=x;
}
void mulfac(int x){
++a2[x];
if(x>mx)mx=x;
}
void f2(int w,int pa){
for(int i=e1[w];i;i=enx[i]){
int u=es[i];
if(u!=pa)f2(u,w);
}
cp=;
for(int i=e1[w],d=;i;i=enx[i]){
int u=es[i];
if(u==pa){
d=;
continue;
}
if(d)cs[cp++]=u;
}
for(int i=e1[w];i;i=enx[i]){
int u=es[i];
if(u==pa)break;
cs[cp++]=u;
}
if(_c[w]){
if(w==rt){
u64 p0=,pp=;
for(int i=;i<=cp;++i){
h1[i]=h1[i+cp]=h2[i]=h2[i+cp]=h[cs[i-]];
pp*=p0;
}
for(int i=;i<=cp*;++i)h1[i]+=h1[i-]*p0;
for(int i=cp*;i;--i)h2[i]+=h2[i+]*p0;
int c=;
for(int i=cp;i<cp*;++i)c+=(h1[i]-h1[i-cp]*pp==h1[cp]);
for(int i=cp;i;--i)c+=(h2[i]-h2[i+cp]*pp==h1[cp]);
mul(c);
}else{
u64 h1=,h2=;
for(int i=;i<cp;++i)h1=h1*+h[cs[i]];
for(int i=cp-;i>=;--i)h2=h2*+h[cs[i]];
if(h1==h2)mul();
h[w]=std::min(h1,h2);
h[w]^=h[w]>>^h[w]*<<^41546541735416351llu;
}
}else{
std::sort(cs,cs+cp,cmp_h);
h[w]=;
for(int i=,j=;i<cp;i=j){
for(;j<cp&&h[cs[i]]==h[cs[j]];++j);
mulfac(j-i);
}
for(int i=;i<cp;++i)h[w]=h[w]*+h[cs[i]];
h[w]^=h[w]>>^h[w]*<<^12218653252112541llu;
}
}
int _(){
int x=,c=getchar();
while(c<)c=getchar();
while(c>)x=x*+c-,c=getchar();
return x;
}
int ps[N],pp,mp[N],ed[N];
bool np[N];
int main(){
n=_();m=_();
n1=n;
for(int i=;i<m;++i){
pp=_();
for(int j=;j<pp;++j)ps[j]=_();
for(int j=;j<pp-;++j)ae(e0,ps[j],ps[j+]);
}
for(int w=;w<=n;++w){
for(int i=e0[w];i;i=enx[i]){
int u=es[i];
if(ed[u]!=w)ed[u]=w;
else if(u>w)--a1[];
}
}
tj(,);
f1(,);
if(cgp==){
if(_c[cg[]])rt=cg[];
else if(_c[cg[]])rt=cg[];
else{
rt=++n1;
ae(e1,n1,cg[]);
ae(e1,n1,cg[]);
de(e1,cg[],cg[]);
de(e1,cg[],cg[]);
}
}else rt=cg[];
f1(rt,);
f2(rt,);
for(int i=mx;i;--i)a2[i]+=a2[i+];
for(int i=mx;i;--i)a2[i]+=a1[i];
pp=;
for(int i=;i<=mx;++i){
if(!np[i])ps[pp++]=mp[i]=i;
for(int j=;j<pp&&i*ps[j]<=mx;++j){
np[i*ps[j]]=;
mp[i*ps[j]]=ps[j];
if(i%ps[j]==)break;
}
}
for(int i=;i<=mx;++i){
for(int x=i;x>;x/=mp[x])a3[mp[x]]+=a2[i];
}
pp=;
for(int i=;i<=mx;++i)if(a3[i])ps[pp++]=i;
printf("%d\n",pp);
for(int j=;j<pp;++j)printf("%d %lld\n",ps[j],a3[ps[j]]);
return ;
}

bzoj3871: [Neerc2013 C]Cactus Automorphisms || 3899: 仙人掌树的同构的更多相关文章

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

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

  2. 03-树1 树的同构 (C语言链表实现)

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h& ...

  3. PAT 03-树1 树的同构 (25分)

    给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后 ...

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

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

  5. SDUT 3340 数据结构实验之二叉树一:树的同构

    数据结构实验之二叉树一:树的同构 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定两棵树 ...

  6. PTA 深入虎穴 (正解)和树的同构

    在上一篇博客中分享了尝试用单链表修改程序,虽然在Dev上运行没有错误,但是PTA设置的测试点有几个没有通过,具体不清楚问题出现在哪里,所以现在把之前正确的程序放在这里. 7-2 深入虎穴 (30 分) ...

  7. 4337: BJOI2015 树的同构

    题解: 树的同构的判定 有根树从根开始进行树hash 先把儿子的f进行排序 $f[i]=\sum_{j=1}^{k} { f[j]*prime[j]} +num[i]$(我没有仔细想这样是不是树是唯一 ...

  8. [BJOI2015]树的同构

    嘟嘟嘟 判断树的同构的方法就是树上哈希. 如果树是一棵有根树,那么只要从根节点出发dfs,每一个节点的哈希值等于按传统方式算出来的子树的哈希值的结果.需要注意的是,算完子树的哈希值后要先排序再加起来, ...

  9. bzoj4337树的同构

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

随机推荐

  1. 集合list里存储list,要遍历最底层list的对象元素的遍历方法

    package com.wtd; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; im ...

  2. 在主Android Activity中加载Fragment的一般简易方法 ,来模拟一个微信界面。

    在Fragment的生命周期中,需要重点关注onCreate.onCreateView.onViewCreated.Activity与Fragment生命周期在设计模式上大体一致. package c ...

  3. windows 开发者神器 tc – total command和替代品

    作为开发者,windows上开发时,会运用一些神器,比如:total commander,搜索目录.查看文件.批量重命名等非常方便. tc是收费版的,有个免费的替代版本Just manager:htt ...

  4. 使用Socket的简单Web服务器

    Socket类在System.Net.Sockets命名空间 常用的操作 Bind:绑定一个本地的终结点 Listen:进入监听状态,并设置等待队列 Accept:等待一个新连接,当连接到达时,返回一 ...

  5. 简单shell实现局域网IP扫描

    #!/bin/bash network=$1 time=$(date +%H%M%S) for i in $(seq $2 $3) do ping -c 1 -w 2 $network.$i > ...

  6. 算法训练 P1103

      算法训练 P1103   时间限制:1.0s   内存限制:256.0MB      编程实现两个复数的运算.设有两个复数 和 ,则他们的运算公式为: 要求:(1)定义一个结构体类型来描述复数. ...

  7. Centos编译Unix网络编程(第三版)卷1的源代码

    测试环境:Centos 1)在shell中输入./configure然后按回车(注意先让configure有执行权限 chomd 777 configure) 2)依次进入lib.libfree.li ...

  8. iOS被开发者遗忘在角落的NSException-其实它很强大

    NSException是什么? 最熟悉的陌生人,这是我对NSException的概述,为什么这么说呢?其实很多开发者接触到NSException的频率非常频繁,但很多人都不知道什么是NSExcepti ...

  9. SYSTEM\sys\sys.c:33:7: error: expected '(' before 'void' __ASM void MSR_MSP(u32 addr)

    在STM32中的sys.c文件编译报出这个错误时: __ASM void MSR_MSP(u32 addr){ MSR MSP, r0 //set Main Stack value BX r14} 如 ...

  10. CAN中如何计算波特率并配置波特率

    //设置波特率 CAN_InitStructure.CAN_SJW=tsjw; //同步宽度 CAN_InitStructure.CAN_BS1=tbs1; //时间段1 CAN_InitStruct ...