zoukankan      html  css  js  c++  java
  • 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
    %s
    ",ans1+1,ans2+1);
        return 0;
    }
    View Code
  • 相关阅读:
    不可或缺 Windows Native (15)
    不可或缺 Windows Native (14)
    不可或缺 Windows Native (13)
    不可或缺 Windows Native (12)
    不可或缺 Windows Native (11)
    不可或缺 Windows Native (10)
    不可或缺 Windows Native (9)
    不可或缺 Windows Native (8)
    不可或缺 Windows Native (7)
    不可或缺 Windows Native (6)
  • 原文地址:https://www.cnblogs.com/csushl/p/10816758.html
Copyright © 2011-2022 走看看