51nod1869 那些年,我们一起讲的故事

来源:互联网 发布:sql server 2008 视图 编辑:程序博客网 时间:2024/06/05 04:05

首先把trie的广义后缀自动机建出来,那么任选起点在trie上走就是在SAM 的起点开始走。在SAM上求出sg函数,先手必胜当且仅当两个自动机上的节点的sg函数值不同。对第一个SAM的每个节点求出它在第二个SAM上对应的合法的路径条数【注意是路径条数,不是节点个数】,然后按照求字典序k小的方法dfs就可以了。
注意几个细节:
1.后继状态总数会爆long long,但是显然总数只需要存到k
2.一个点可能有好几条字母相同的出边,也就是其实给的不是trie。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define LL long longconst int maxn=500010;int trans[maxn][26],fail[maxn],val[maxn],sg[maxn],trie[maxn][26],rt[2],que[maxn],id[maxn],cnt[maxn],ord[2][maxn],vis[30],corr[maxn],n[2],t1[2],tot;LL size[maxn],sum[maxn],num[maxn][30],k;int add(int x,int u,int fl){    int nu,v,nv;    val[nu=++tot]=val[u]+1;    while (u&&!trans[u][x])    {        trans[u][x]=nu;        u=fail[u];    }    if (!u) fail[nu]=rt[fl];    else    {        v=trans[u][x];        if (val[v]==val[u]+1) fail[nu]=v;        else        {            val[nv=++tot]=val[u]+1;            fail[nv]=fail[v];            fail[nu]=fail[v]=nv;            for (int i=1;i<=26;i++) trans[nv][i]=trans[v][i];            while (u&&trans[u][x]==v)            {                trans[u][x]=nv;                u=fail[u];            }        }    }    return nu;}void build(int fl){    int x,hd,tl,u;    char c[3];    scanf("%d",&n[fl]);    for (int i=0;i<=n[fl];i++)        for (int j=1;j<=26;j++)            trie[i][j]=0;    for (int i=1;i<=n[fl];i++)    {        scanf("%d%s",&x,c);        if (!trie[corr[x]][c[0]-'a'+1]) trie[corr[x]][c[0]-'a'+1]=i;        corr[i]=trie[corr[x]][c[0]-'a'+1];    }    id[0]=rt[fl]=++tot;    que[hd=tl=1]=0;    while (hd<=tl)    {        u=que[hd++];        for (int i=1;i<=26;i++)            if (trie[u][i])            {                id[trie[u][i]]=add(i,id[u],fl);                que[++tl]=trie[u][i];            }    }    for (int i=0;i<=n[fl];i++) cnt[i]=0;    for (int i=rt[fl];i<=tot;i++) cnt[val[i]]++;    for (int i=1;i<=n[fl];i++) cnt[i]+=cnt[i-1];    for (int i=rt[fl];i<=tot;i++) ord[fl][cnt[val[i]]--]=i;    t1[fl]=tot-rt[fl]+1;    for (int i=t1[fl];i>=1;i--)    {        u=ord[fl][i];        memset(vis,0,sizeof(vis));        for (int j=1;j<=26;j++)            if (trans[ord[fl][i]][j])                vis[sg[trans[u][j]]]=1;        for (int j=0;;j++)            if (!vis[j])            {                sg[u]=j;                break;            }    }}void dfs(int fl,int u,LL k){    int x;    if (size[u]>=k)    {        putchar('\n');        if (fl) return;        for (int i=t1[1];i>=1;i--)        {            x=ord[1][i];            size[x]=sum[x]=(sg[x]!=sg[u]);            for (int j=1;j<=26;j++)                sum[x]+=sum[trans[x][j]];        }        dfs(1,rt[1],k);        return;    }    k-=size[u];    for (int j=1;j<=26;j++)        if (sum[trans[u][j]]>=k)        {            putchar('a'+j-1);            dfs(fl,trans[u][j],k);            return;        }        else k-=sum[trans[u][j]];}int main(){    //freopen("c.in","r",stdin);    //freopen("c.out","w",stdout);    int u;    scanf("%lld",&k);    build(0);    build(1);    for (int i=t1[1];i>=1;i--)    {        u=ord[1][i];        num[u][sg[u]]=num[u][27]=1;        for (int j=1;j<=26;j++)            for (int k=0;k<=27;k++)                num[u][k]+=num[trans[u][j]][k];    }    for (int i=t1[0];i>=1;i--)    {        u=ord[0][i];        sum[u]=size[u]=num[rt[1]][27]-num[rt[1]][sg[u]];        for (int j=1;j<=26;j++)        {            sum[u]+=sum[trans[u][j]];            if (sum[u]>k+10) sum[u]=k+10;        }    }    if (k>sum[1])    {        printf("K is too large!\n");        return 0;    }    dfs(0,1,k);}