HihoCoder1466-后缀自动机六·重复旋律9

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段字符构成的字符串。

现在小Hi已经不满足于单单演奏了!他通过向一位造诣很高的前辈请教,通过几周时间学习了创作钢琴曲的基本理论,并开始对曲目做改编或者原创。两个月后,小Hi决定和前辈进行一场创作曲目的较量!

规则是这样的,有两部已知经典的作品,我们称之为A和B。经典之所以成为经典,必有其经典之处。

刚开始,纸上会有一段A的旋律和一段B的旋律。两人较量的方式是轮流操作,每次操作可以选择在纸上其中一段旋律的末尾添加一个音符,并且要求添加完后的旋律依然是所在作品的旋律(也就是A或B的一个子串)。谁词穷了(无法进行操作)就输了。

小Hi和前辈都足够聪明,但是小Hi还是太年轻,前辈打算教训他一顿。前辈表示会和小Hi进行K次较量,只要小Hi赢了哪怕一次就算小Hi获得最终胜利。但是前提是开始纸上的两段旋律需要他定。小Hi欣然同意,并且表示每次较量都让前辈先操作。

前辈老谋深算,显然是有备而来。他已经洞悉了所有先手必胜的初始(两段)旋律。第i天前辈会挑选字典序第i小的初始(两段)旋律来和小Hi较量。那么问题来了,作为吃瓜群众的你想知道,最后一天即第K天,前辈会定哪两个旋律呢?

初始时两段旋律的字典序比较方式是先比较前一个旋律字典序,一样大则比较后一旋律的字典序。

输入

第一行包含一个正整数,K。K<=10^18

第二行包含一个非空的仅有小写字母构成的字符串,表示作品A。|A|<=10^5

第三行包含一个非空的仅有小写字母构成的字符串,表示作品B。|B|<=10^5

输出

Sample Input

5
ab
cd

Sample Output

a
cd

输出共两行,每行一个字符串(可能为空),表示答案。

如果无解则只输出一行"NO"。

题解:

后缀自动机的next指针DAG图上求SG 函数值DFS找字典序第K小先手必胜子串对 ;

题目大意:给定两个字符串A,B ,现在要求分别用A,B 的两个子串X,Y 进行游戏,问字典序第K KK小的两个先手必胜的X,Y 是哪两个.其中X,Y的字典序比较方式为先比较第一个串的字典序,后比较第二个串的字典序.
游戏规则为,先手开始游戏,两人轮流加字符,先无法操作的人输.其中每一轮操作的规则为,任选X,Y 中的一个,往后面加一个字符使得X 还是A的子串,Y 还是B的子串.

考虑直接对A,B 都建立SAM,由于是要求它们的子串胜负,所以考虑对于SAM上的每个状态求出它的胜负,利用SG函数即可.整个游戏的胜负可以通过SG定理来合并.

值得注意的是,很容易发现SAM上任意一个状态的sg值都不会超过字符集大小,因为SAM上每个状态最多连字符集大小条出边.

得到了每一个状态的胜负之后,我们考虑在一个SAM上如何找到第k 小的必胜子串,这个问题类似于第k小子串问题,只是我们预处理每一个状态开始的所有子串的时候要忽略必败态,也就是忽略sg=0 时候的情况.两个串的时候,我们可以考虑先确定答案在第一个SAM上的位置,再确定第二个.不过在第一个SAM上跑的时候,要直接减掉可以与正在遍历的状态组成必胜态的子串数量,也就是说sg≠ 0  ​ ,这个时候我们就需要记录sg为每一个数时的子串数量了.

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define csiz 27
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
char s[maxn],ans1[maxn<<1],ans2[maxn<<1];
int tmp;
ll k;

struct SAM{
    int fa[maxn<<1],nxt[maxn<<1][26],l[maxn<<1];
    int last,ct,sg[maxn<<1],flag[maxn<<1][csiz];
    ll sum[maxn<<1],cnt[maxn<<1][csiz];

    void Init()
    {
        last=ct=1;
        memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        memset(nxt[1],0,sizeof(nxt[1]));
        fa[1]=l[1]=0;
    }
    
    int NewNode()
    {
        ct++;
        memset(nxt[ct],0,sizeof(nxt[ct]));
        fa[ct]=l[ct]=0;
        return ct;
    }
    
    void Insert(int ch)
    {
        int p=last,np=NewNode();
        last=np; l[np]=l[p]+1;
        while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p];
        if(!p) fa[np]=1;
        else
        {
            int q=nxt[p][ch];
            if(l[q]==l[p]+1) fa[np]=q;
            else
            {
                int nq=NewNode();
                memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
                fa[nq]=fa[q];
                l[nq]=l[p]+1;
                fa[np]=fa[q]=nq;
                while(nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p];
            }
        }
    }
    
    int GetSG(int k)
    {
          if(sg[k]^-1) return sg[k];
          for(int i=0;i<=26;++i) flag[k][i]=0;
          for(int i=0;i<26;++i)
            if(nxt[k][i])
        {
            flag[k][GetSG(nxt[k][i])]=1;
            for(int j=0;j<=26;++j)
                  cnt[k][j]+=cnt[nxt[k][i]][j]; 
          }
          int i=0;
          while(flag[k][i]) ++i;
          sg[k]=i;
        ++cnt[k][sg[k]];
        for(int i=0;i<=26;++i) sum[k]+=cnt[k][i];
        return sg[k];
      }
      
    void Build(char*ss)
    {
        Init();
        int len=strlen(ss);
        for(int i=0;i<len;++i) Insert(ss[i]-'a');
        for(int i=1;i<=ct;++i) sg[i]=-1;
        GetSG(1);
    }
}sa,sb;

ll calc(int a,int b)
{
      ll sum=0;
      for(int i=0;i<=26;++i)
        sum+=sa.cnt[a][i]*(sb.sum[b]-sb.cnt[b][i]);
      return sum;
}

int dfs1(int x,int pos)
{
      ll sum1=sb.sum[1]-sb.cnt[1][sa.sg[x]],sum2; 
      if(sum1>=k) return x;
      else k-=sum1;
      for(int i=0;i<26;++i)
      {
        if(sa.nxt[x][i])
        {
              sum2=calc(sa.nxt[x][i],1);
              if(sum2<k) k-=sum2;
              else 
              {
                  ans1[pos]='a'+i;
                  return dfs1(sa.nxt[x][i],pos+1);
              }
        }
    }
      return -1;
}

int dfs2(int x,int pos)
{
    k-=sa.sg[tmp]!=sb.sg[x];
    if(k==0) return x;
    ll sum;
    for(int i=0;i<26;++i)
        {
        if(sb.nxt[x][i])
        {
              sum=sb.sum[sb.nxt[x][i]]-sb.cnt[sb.nxt[x][i]][sa.sg[tmp]];
              if(sum<k) k-=sum;
              else 
              {
                ans2[pos]='a'+i;
                return dfs2(sb.nxt[x][i],pos+1);
              }
        }
    }
       return -1;
}

int main()
{
    scanf("%lld",&k);
    scanf("%s",s);
    sa.Build(s);
    scanf("%s",s);
    sb.Build(s);
    tmp=dfs1(1,1);
    if(tmp^-1) dfs2(1,1);
    if(tmp==-1) puts("NO");
    else printf("%s\n%s\n",ans1+1,ans2+1);
    return 0;
}
View Code

 

posted @ 2019-05-05 22:22  StarHai  阅读(458)  评论(0编辑  收藏  举报