[仙人掌同构 Hash] Codeforces Gym 100307 NEERC 13 C. Cactus Automorphisms

其实就是BZOJ3899的加强版
当时写的东西真是不敢恭维

还是看Po姐的题解
我们把仙人掌拆成圆方树 就可以直接用树hash来做
先找重心 因为我写的时候把两个点也当做点双 那么所有边都是圆方相接如果重心有两个 去代表环的方点就好了
接下来是hash 圆点没问题 子树排完序hash 顺带记一下如果有相同 答案乘上出现次数的阶乘
不是根的方点 也就是一个环 是有顺序的 不能排序 然后看一下翻转是不是一样 乘个2
最后如果根是一个方点 那么我们要考虑环的同构 环翻转前后跑一通KMP就知道有几种情况是循环同构的 乘上答案

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<stack>
#include<set>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> abcd;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=100005,P=1e9+3;

int prime[N],num;
int q[N];
int vst[N],d[N];
inline void Pre(int n){
  for (int i=2;i<=n;i++){
    if (!vst[i]) prime[++num]=i,d[i]=i;
    for (int j=1;j<=num && (ll)i*prime[j]<=n;j++){
      vst[i*prime[j]]=1; d[i*prime[j]]=prime[j];
      if (i%prime[j]==0) break;
    }
  }
}

int _q[N];

inline void Print(int n){
  for (int i=n;i;i--){
    _q[i]+=_q[i+1];
    if (_q[i]){
      int t=i;
      while (t>1)
    q[d[t]]+=_q[i],t/=d[t];
    }
  }
  int tot=0;
  for (int i=1;i<=n;i++) tot+=q[i]!=0;
  printf("%d\n",tot);
  for (int i=1;i<=n;i++)
    if (q[i])
      printf("%d %d\n",i,q[i]);
}

const ull ORI1=2333,ORI2=23333;
const ull BASE1=999911657,BASE2=999911659;
const ull END1=18357,END2=81643;

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum=1;
#define V G[p].v
inline void add(int u,int v){
  int p=++inum; G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int n,m,bcc;

int size[N],minv=1<<30,rt;

inline void Root(int u,int fa){
  size[u]=1; int maxv=0;
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa)
      Root(V,u),size[u]+=size[V],maxv=max(maxv,size[V]);
  maxv=max(maxv,n+bcc-size[u]);
  if (maxv<minv || (maxv==minv && u>rt)) rt=u,minv=maxv;
}

ull lst[N]; int pnt;
ull H[N];

int nxt[N];
ull b[N];

inline int KMP(ull *a,int n){
  int k=0; nxt[1]=0;
  for (int i=2;i<=n;i++){
    while (k && a[k+1]!=a[i]) k=nxt[k];
    if (a[k+1]==a[i]) k++;
    nxt[i]=k;
  }
  for (int i=1;i<=n;i++) b[i]=b[n+i]=a[i];
  k=0; int ret=0;
  for (int i=1;i<n+n;i++){
    while (k && a[k+1]!=b[i]) k=nxt[k];
    if (a[k+1]==b[i]) k++;
    if (k==n) k=nxt[k],ret++;
  }
  if (n>2){
    reverse(b+1,b+n+n+1); k=0;
    for (int i=1;i<n+n;i++){
      while (k && a[k+1]!=b[i]) k=nxt[k];
      if (a[k+1]==b[i]) k++;
      if (k==n) k=nxt[k],ret++;
    }
  }
  return ret;
}

inline void dfs(int u,int fa){
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa)
      dfs(V,u);
  pnt=0;
  if (u<=n){
    for (int p=head[u];p;p=G[p].next)
      if (V!=fa)
    lst[++pnt]=H[V];
    sort(lst+1,lst+pnt+1);
    int tmp=0;
    for (int i=1;i<=pnt;i++){
      tmp++;
      if (i==pnt || lst[i]!=lst[i+1])
    _q[tmp]++,tmp=0;
    }
    H[u]=ORI1;
    for (int i=1;i<=pnt;i++) (((H[u]*=BASE1)+=lst[i])^=lst[i])+=lst[i];
    ((H[u]+=END1)*=END1)^=END1;
  }else if (u!=rt){
    int k;
    for (int p=head[u];p;p=G[p].next)
      if (V==fa) { k=p; break; }
    for (int p=G[k].next;p;p=G[p].next)
      lst[++pnt]=H[V];
    for (int p=head[u];p!=k;p=G[p].next)
      lst[++pnt]=H[V];
    ull H1,H2;
    H1=ORI2;
    for (int i=1;i<=pnt;i++) (((H1*=BASE2)+=lst[i])^=lst[i])+=lst[i];
    ((H1+=END2)*=END2)^=END2;
    H2=ORI2;
    for (int i=pnt;i;i--) (((H2*=BASE2)+=lst[i])^=lst[i])+=lst[i];
    ((H2+=END2)*=END2)^=END2;
    H[u]=min(H1,H2);
    if (pnt>=2 && H1==H2)
      _q[2]++;
  }else{
    for (int p=head[u];p;p=G[p].next)
      if (V!=fa)
    lst[++pnt]=H[V];
    int ret=KMP(lst,pnt),t=ret;
    for (int i=1;i<=num && prime[i]<=ret;i++)
      while (t%prime[i]==0)
    t/=prime[i],q[prime[i]]++;
  }
}

namespace Cac{
  struct edge{
    int u,v,next;
  }G[N<<1];
  int head[N],inum=1;
  inline void add(int u,int v){
    int p=++inum; G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
  }
  set<abcd> Set;
  inline void Add(int u,int v){
    if (u>v) swap(u,v); if (!Set.count(abcd(u,v))) Set.insert(abcd(u,v)),add(u,v),add(v,u);
  }
  int pre[N],low[N],clk;
  stack<int> sta;
  inline void dfs(int u,int fa){
    pre[u]=low[u]=++clk; sta.push(u);
    for (int p=head[u];p;p=G[p].next)
      if (p^fa^1)
    if (!pre[V]){
      dfs(V,p);
      low[u]=min(low[u],low[V]);
      if (low[V]>=pre[u]){
        ++bcc;
        while (sta.top()!=u){
          int t=sta.top(); sta.pop();
          ::add(n+bcc,t),::add(t,n+bcc);
          if (t==V) break;
        }
        ::add(n+bcc,u); ::add(u,n+bcc);
      }
    }else
      low[u]=min(low[u],pre[V]);
  }
  inline void Read(){
    int k,x,y;
    read(m);
    while (m--){
      read(k); read(x);
      for (int i=2;i<=k;i++)
    read(y),Add(x,y),x=y;
    }
    dfs(1,0);
  }
}

int main(){
  freopen("cactus.in","r",stdin);
  freopen("cactus.out","w",stdout);
  read(n); Pre(n<<1);
  Cac::Read();
  Root(1,0);
  dfs(rt,0);
  Print(n<<1);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值