bzoj3899: 仙人掌树的同构

Description

首先,先介绍仙人掌树。仙人掌树是一张无向图,但是每个节点最多只会在一个环里面,而且这张图的环全部都是简单环,即A->B->C->A这种。

比如下图就是一颗仙人掌树。

好的,知道了仙人掌树之后,我们现在要计算一个东西。

我们现在已经知道了一个N个节点的仙人掌树,称作为原图。接下来,我们要用1-N的一个排列A[1]-A[N]去变换这棵树,具体的,如果原图中有一条边i-j,那么变换出来的图中必须有一条A[i]-A[j]的边。同样的,如果变换出来的图中有一条A[i]-A[j]的边,那么原图中必有一条i-j的边。(简单而言就是点重新编号)

小A为了超脱宇宙的束缚,必须要知道,有多少种排列,可以使得变换出来的新图和原图是一模一样的,具体的,原图中如果存在一条i-j的边,新图也存在一条i-j的边,新图中存在一条i-j的边,原图中也存在i-j的边。

方案数目答案mod 1000000003。

Input

第一行有两个正整数,N和M,节点个数和边的个数。

接下来M行,每行有2个正整数S,T,表示一条原图的无向边。数据保证没有重边。

Output

一行一个正整数表示方案书目。

转成圆方树的自同构,找到重心变为有根树,对一个圆点,其k棵子树相同对答案贡献为k!,对一个方点,若为根直接考虑旋转和翻转的每种情况是否与原先同构,否则只考虑翻转后是否同构,贡献为2,全部用hash判定并将贡献累乘即可。方点相邻的点是有序的,在计算时要把相邻点排成环上的顺序的再处理。

#include<cstdio>
#include<algorithm>
typedef unsigned long long u64;
const int N=20007,P=1000000003;
int n,m,fac[N];
int es[N],enx[N],e0[N],e1[N],ep=2;
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=0,ss[N],sp=0,n1,ri[N];
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==pa)continue;
        if(!dfn[u]){
            tj(u,w);
            if(ss[sp]==u)--sp,ae(e1,w,u);
        }else if(dfn[u]<dfn[w]){
            int rp=0;
            ae(e1,u,++n1);
            _c[n1]=1;
            while(dfn[ss[sp]]>dfn[u])ri[ss[sp]]=++rp,ae(e1,ss[sp--],n1);
        }
    }
}
int sz[N],cg[3],cgp=0,rt;
void f1(int w,int pa){
    bool is=1;
    sz[w]=1;
    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]*2>n)is=0;
    }
    if(sz[w]*2<n)is=0;
    if(is)cg[cgp++]=w;
}
u64 h[N],h1[N],h2[N];
int cs[N],cp,ans=1;
bool cmp_h(int a,int b){return h[a]<h[b];}
bool cmp_ri(int a,int b){return ri[a]<ri[b];}
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=0;
    for(int i=e1[w];i;i=enx[i]){
        int u=es[i];
        if(u!=pa)cs[cp++]=u;
    }
    if(_c[w]){
        if(w!=rt)
        for(int i=0;i<cp;++i)ri[cs[i]]=(ri[cs[i]]-ri[pa]+(cp+1))%(cp+1);
        std::sort(cs,cs+cp,cmp_ri);
        if(w==rt){
            u64 p0=29399999,pp=1;
            for(int i=1;i<=cp;++i){
                h1[i]=h1[i+cp]=h2[i]=h2[i+cp]=h[cs[i-1]];
                pp*=p0;
            }
            for(int i=1;i<=cp*2;++i)h1[i]+=h1[i-1]*p0;
            for(int i=cp*2;i;--i)h2[i]+=h2[i+1]*p0;
            int c=0;
            for(int i=cp;i<cp*2;++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]);
            ans=u64(ans)*c%P;
        }else{
            u64 h1=0,h2=0;
            for(int i=0;i<cp;++i)h1=h1*1399913+h[cs[i]];
            for(int i=cp-1;i>=0;--i)h2=h2*1399913+h[cs[i]];
            if(h1==h2)ans=ans*2%P;
            h[w]=std::min(h1,h2);
            h[w]^=h[w]>>13^h[w]*17<<31^41546541635416351llu;
        }
    }else{
        std::sort(cs,cs+cp,cmp_h);
        h[w]=0;
        for(int i=0,j=0;i<cp;i=j){
            for(;j<cp&&h[cs[i]]==h[cs[j]];++j);
            ans=u64(ans)*fac[j-i]%P;
        }
        for(int i=0;i<cp;++i)h[w]=h[w]*1844677+h[cs[i]];
        h[w]^=h[w]>>17^h[w]*11<<23^12218653252152541llu;
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=fac[0]=1;i<=n;++i)fac[i]=fac[i-1]*u64(i)%P;
    n1=n;
    for(int i=0,a,b;i<m;++i){
        scanf("%d%d",&a,&b);
        ae(e0,a,b);
    }
    tj(1,0);
    f1(1,0);
    if(cgp==2){
        if(_c[cg[0]])rt=cg[0];
        else if(_c[cg[1]])rt=cg[1];
        else{
            rt=++n1;
            ae(e1,n1,cg[0]);
            ae(e1,n1,cg[1]);
            de(e1,cg[0],cg[1]);
            de(e1,cg[1],cg[0]);
        }
    }else rt=cg[0];
    f2(rt,0);
    printf("%d\n",ans);
    return 0;
}
时间: 2024-11-15 00:36:22

bzoj3899: 仙人掌树的同构的相关文章

BZOJ 3899 仙人掌树的同构 仙人掌同构+KMP算法

题目大意:给定一棵仙人掌,求有多少自同构 仙人掌同构问题= = 曾经出过一个判断两个仙人掌是否同构的题,感觉和这个题很类似 首先假设这是一棵树,考虑怎么做 我们首先找到树的重心(如果有两个就在中间加一个点变成一个) 然后把树Hash 对于一棵树 如果某一哈希值的子树有k个 就把答案乘上一个k! 现在变成了仙人掌,那么我把每个环变成一个红点连向环上的所有点,然后把原先环上的边拆除,可以得到一棵树,按树同构做就行了 为了区分红点和普通点的区别,需要为红点设置不同的哈希参数 但是这样有一个BUG,就是

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

数据结构实验之二叉树一:树的同构 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. Input 输入数据包含

03-树1 树的同构

给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. 输入格式: 输入给出2棵二叉树树的信息.对于每棵树,首先在一行中给出一个非负整数NN (\le 10≤10),即该树的结点数(此时假设结点从0到N-1N−1编号):随后NN行,第ii行对应编号第ii个结点,给出该结点中存储的

5-3 树的同构 (25分)

5-3 树的同构   (25分) 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. 输入格式: 输入给出2棵二叉树树的信息.对于每棵树,首先在一行中给出一个非负整数NN (\le 10≤10),即该树的结点数(此时假设结点从0到N-1N?1编号):随后NN行,第i

ZOJ--3602--Count the Trees【DFS+Hash】树的同构

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3602 题意:给出一棵有n个节点的二叉树和一棵有m个节点的二叉树,给出每个节点的左右子树信息,问这两棵树有几个相同的子树. 思路:树的同构,比赛时没想法,赛后看的别人的解题报告.实际上是给每个节点的左右子树一个哈希值,不用像字符串哈希那么麻烦,直接给每个子树一个数字标记就行了,用map映射每个节点的左子树和右子树信息对应一个标记值,用DFS给两棵树的每个节点都赋一个哈

BZOJ 4337 树的同构

很明显,这应该是一道模版题(因为我很快就在一本书上找到了这道题的模版),不过令我比较奇怪的大概是有根树和无根树的概念,以及在这道题目中根有卵用吗? (看来树这一块的知识还是要补一下). 树的同构很明显应该是用hash来判断的,当然了,不同的人设计的hash函数不同了.这道题正确的应该是要在树的重心上面跑这道题的模版,(如果你要问我树的重心是啥,我只能跟你说,如果我知道的话,下面这份代码就不会把几乎所有的点都跑一次了,但是由于N<=50,M <= 50 很明显这样跑完还是很快的,事实证明也只跑了

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

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdbool.h> 5 6 typedef char ElemType; 7 8 typedef struct BinTree 9 { 10 ElemType data; 11 struct BinTree *left; 12 struct BinTree *right; 13 }BinTree; 14

PTA 树的同构(25 分)

7-1 树的同构(25 分) 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. 输入格式: 输入给出2棵二叉树树的信息.对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N?1编号):随后N行,第i行对应编号第i个结点,给出

树的同构

给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. 输入格式: 输入给出2棵二叉树树的信息.对于每棵树,首先在一行中给出一个非负整数N (≤),即该树的结点数(此时假设结点从0到N?1编号):随后N行,第i行对应编号第i个结点,给出该结点中存储的1个英文大写字母.其