[广义后缀自动机 SG值] 51Nod 1869 那些年,我们一起讲的故事

简直 在众人的帮助下理解了一个假题意 看完题解知道真题意
大概是每次加一个字符 这个串仍然要是Trie树的子串
直接建SAM 然后求SG值 不超过度数+1 也就是27
然后先手必胜 两边SG不同 那么按字典序数一数就好了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cassert>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;

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;
}
inline void read(ll &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;
}
inline void read(char &x){
  for (x=nc();!(x>='a' && x<='z');x=nc());
}

const int N=400005;

struct SAM{
  struct state{
    int link,len,next[26];
  }st[N];
  int last,ncnt;
  void Extend(int c){
    if (st[last].next[c]){
      int q=st[last].next[c];
      if (st[q].len==st[last].len+1)
    last=q;
      else{
    int nq=++ncnt;
    st[nq].link=st[q].link; st[q].link=nq;
    st[nq].len=st[last].len+1;
    for (int i=0;i<26;i++) st[nq].next[i]=st[q].next[i];
    for (int p=last;p && st[p].next[c]==q;p=st[p].link)
      st[p].next[c]=nq;
    last=nq;
      }
    }else{
      int cur=++ncnt,p;
      st[cur].len=st[last].len+1;
      for (p=last;p && !st[p].next[c];p=st[p].link)
    st[p].next[c]=cur;
      if (!p)
    st[cur].link=1;
      else{
    int q=st[p].next[c];
    if (st[q].len==st[p].len+1)
      st[cur].link=q;
    else{
      int nq=++ncnt;
      st[nq].link=st[q].link;
      st[nq].len=st[p].len+1;
      for (int i=0;i<26;i++) st[nq].next[i]=st[q].next[i];
      for (;p && st[p].next[c]==q;p=st[p].link)
        st[p].next[c]=nq;
      st[cur].link=nq; st[q].link=nq;
    }
      }
      last=cur;
    }
  }
  int tmp[N],sum[N],c[N];
  inline void Sort(){
    for (int i=1;i<=ncnt;i++) c[st[i].len]++;
    for (int i=1;i<=100000;i++) c[i]+=c[i-1];
    for (int i=1;i<=ncnt;i++) tmp[c[st[i].len]--]=i; 
  }
  ll cnt[N][27],tot[N]; int sg[N];
  int clk,vst[27];
  void Pre(){
    for (int i=ncnt;i;i--){
      int x=tmp[i]; ++clk;
      for (int j=0;j<26;j++){
    if (!st[x].next[j]) continue;
    vst[sg[st[x].next[j]]]=clk;
    tot[x]+=tot[st[x].next[j]];
    for (int k=0;k<=26;k++)
      cnt[x][k]+=cnt[st[x].next[j]][k];
      }
      sg[x]=-1;
      for (int j=0;j<=26;j++) if (vst[j]!=clk) { sg[x]=j; break; }
      assert(sg[x]!=-1);
      cnt[x][sg[x]]++; tot[x]++;
    }
  }
  ll Tot(int x,int c){
    return tot[x]-cnt[x][c];
  }
}A,B;

char sa[N],sb[N];
int na,nb;

inline void solve(ll K,int c){
  int x=1;
  while (1){
    if (K==1 && B.sg[x]!=c)
      return;
    K-=B.sg[x]!=c;
    for (int j=0;j<26;j++){
      if (!B.st[x].next[j]) continue;
      if (K<=B.Tot(B.st[x].next[j],c)){
    x=B.st[x].next[j]; sb[++nb]='a'+j; break;
      }else{
    K-=B.Tot(B.st[x].next[j],c);
      }
    }
  }
}

ll Sum[N],Val[N];

inline void Solve(ll K){
  for (int i=A.ncnt;i;i--){
    int x=A.tmp[i];
    Sum[x]=Val[x]=B.Tot(1,A.sg[x]);
    for (int j=0;j<26;j++){
      if (!A.st[x].next[j]) continue;
      Sum[x]+=Sum[A.st[x].next[j]];
      if (Sum[x]>1e18) Sum[x]=1e18;
    }
  }
  if (K>Sum[1]){
    printf("K is too large!\n"); exit(0);
  }
  int x=1;
  while (1){
    if (K<=Val[x]){
      solve(K,A.sg[x]); return;
    }else{
      K-=Val[x];
      for (int j=0;j<26;j++){
    if (!A.st[x].next[j]) continue;
    if (K<=Sum[A.st[x].next[j]]){
      sa[++na]='a'+j; x=A.st[x].next[j]; break;
    }else{
      K-=Sum[A.st[x].next[j]];
    }
      }
    }
  }
}

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

inline void dfs(int u,SAM &A){
  int tmp=A.last; if (u) A.Extend(val[u]);
  for (int p=head[u];p;p=G[p].next)
    dfs(V,A);
  A.last=tmp;
}

int main(){
  ll K; int n,p; char c;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(K);
  read(n);
  for (int i=1;i<=n;i++)
    read(p),read(c),add(p,i,++inum),val[i]=c-'a';
  A.last=A.ncnt=1; dfs(0,A);
  A.Sort(); A.Pre();
  cl(head); inum=0;
  read(n);
  for (int i=1;i<=n;i++)
    read(p),read(c),add(p,i,++inum),val[i]=c-'a';
  B.last=B.ncnt=1; dfs(0,B);
  B.Sort(); B.Pre();
  Solve(K);
  printf("%s\n%s\n",sa+1,sb+1);
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值