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
  • 相关阅读:
    【转】在.NET使用JSON作为数据交换格式
    类似QQ管家页面jquery图片轮换实例
    jquery+ajax 文件上传功能(无ifram嵌套)
    sql另辟蹊,持续更新
    【转】JavaScript 中 function 的动态执行
    八款js框架比较
    【转】怎样写出可维护的面向对象javascript
    【转】程序员该如何定位?
    WF4.0实战系列索引
    Microsoft Help Viewer help查看器所需的内容文件缺失或者损坏 解决办法
  • 原文地址:https://www.cnblogs.com/csushl/p/10816758.html
Copyright © 2011-2022 走看看