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<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,m,p[N],t,dfn[N],low[N],stk[N],top,cnt,tot,ans=1;
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]=1;
        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=0;
        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]<<1)>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=0;
            for (int i=p[k];i;i=edge[i].nxt)
            if (edge[i].to!=from) a[++cnt]=hash[edge[i].to];
            sort(a+1,a+cnt+1);
            for (int i=1;i<=cnt;i++) hash[k]=hash[k]*p1+a[i];
            hash[k]=hash[k]*p2+size[k];
            for (int i=1;i<=cnt;i++)
            {
                int t=i;
                while (t<cnt&&a[t+1]==a[i]) t++;
                int fac=1;
                for (int j=1;j<=t-i+1;j++) fac=1ll*fac*j%P;
                ans=1ll*ans*fac%P;
                i=t;
            }
        }
        else if (k!=from)
        {
            if (BCC[k-n].size()>2)
            {
                int pos;
                for (int i=0;i<BCC[k-n].size();i++)
                if (BCC[k-n][i]==from) {pos=i;break;}
                ull hash1=0,hash2=0;int x=pos,y=pos;
                for (int i=1;i<BCC[k-n].size();i++)
                {
                    x--;if (x<0) x+=BCC[k-n].size();
                    hash1=hash1*p1+hash[BCC[k-n][x]];
                    y++;if (y==BCC[k-n].size()) y=0;
                    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=0;
            for (int i=0;i<BCC[k-n].size();i++)
            hash1=hash1*p1+hash[BCC[k-n][i]];
            int cnt=0;
            for (int i=0;i<BCC[k-n].size();i++) 
            {
                int x=i,y=i;ull h1=0,h2=0;
                for (int j=0;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=0;
                    y--;if (y<0) y+=BCC[k-n].size();
                }
                if (h1==hash1) cnt++;
                if (h2==hash1) cnt++;
            }
            if (BCC[k-n].size()==2) cnt/=2;
            ans=1ll*ans*cnt%P;
        }
    }
    void solve()
    {
        make(1,1);
        int root=findroot(1,1,size[1]);
        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=1;i<=m;i++)
    {
        int x=read(),y=read();
        addedge(x,y),addedge(y,x);
    }
    tarjan(1);
    for (int i=1;i<=tot;i++)
        for (int j=0;j<BCC[i].size();j++)
        blocktree::addedge(n+i,BCC[i][j]);
    blocktree::solve();
    cout<<ans;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/10291696.html
今日推荐