题目描述:
想象男女主人公各自的平行世界,两个世界都有各自最初的起点,然后两人会有多种不同的选择按时间顺序去经历各自的世界。我们假定一个当前的世界状态,那么我们现在知道每个状态下一时间段会发生的若干可能的故事。我们称片段为从任意一个状态按时间顺序往后到另一个状态所发生的一系列故事。当然,平行世界有尽头,也就是接下来没有任何可能的故事发生所对应的状态。
有一段时间男女主人公开始想象平行世界所发生的事。尽管真实世界里的故事是男女主人公最终没有走到一起。
每晚,男主都要和女主煲电话粥轮流讲故事,他们会先选择一个各自世界中的一个片段作为初始,因为是想象,所以初始片段不一定要从最初的起点开始。我们记女主的为A,男主的为B,那么初始片段(注意这里指的都是片段,也就是一系列故事)记为(A,B)。然后两人开始轮流想象接下来的故事讲给对方听(女主先),由于双方都彼此十分了解,故事也是随性所接,所以不一定要讲自己的故事(比如,双方先把女主的故事讲完,再讲男主的故事,或者交叉讲),但一定要是各自世界中的某一个片段。两人会一直讲到世界的尽头,即不能继续下去时结束通话。比如说当前有三个事件,a表示见面,b表示约会,c表示分手。当前的某一个片段是a,则可以见面后直接分手即ac,也可以是见面后约会即ab,但必须保证新的片段(ac或者ab)在对应的世界中作为片段出现过。两人都希望自己能成为挂掉电话前最后讲述故事的那个人,因此两人的讲故事都是精心挑选的,我们假定双方均了解各自双方的平行世界的所有故事。
为了不使通话内容重复,两人决定每晚的初始片段都要不同,当然由于女生比较井井有条,凡事喜欢按字典顺序从小到大来,所以男主让女主帮他决定所选择的初始片段。然而,机智的女主写下了所有可能的初始片段并偷偷将所有男主能成为最后讲述故事的人所对应的初始片段删去了,也就是每次只会选择自己能成为最后讲述故事的人所对应的初始片段。于是我们想知道第K天时,两人的初始片段。
故事按小写字母标识。
两个片段相同当且仅当长度相同且每一个故事均相同。
一个片段A小于片段B的条件是第一个不相同的故事所对应的字母于字母表中A的在B的前面。
初始片段(A,B)小于另一个(A’,B’)的条件是A小于A’;或者A等于A’且B小于B’。【样例解释】
前5天的初始状态分别为:(“”,“c”),(“”,“cd”),(“”,“d”),(“a”,“”),(“a”,“cd”) 。
输入
第一行一个正整数K,表示所问的是第K天。 接下来输入分两块,分别表示男女主人公的平行世界。 每一块格式如下: 第一行为一个整数n,表示故事个数。 接下来n行描述故事,每个故事用一个整数pre,和一个小写字符event表示。Pre表示这个故事发生前的状态编号(最初的起点编号为0,然后按出现顺序依次类推),event表示故事的标识。 K<=10^18,n<=10^5。输入保证合法。
输出
若无解则输出”K is too large!”。 否则输出两行字符串,分别表示第K天时,女主和男主的初始状态。
输入样例
5 3 0 a 0 b 1 a 2 0 c 1 d
输出样例
a cd
题解:
首先把trie的广义后缀自动机建出来,那么任选起点在trie上走就是在SAM 的起点开始走。在SAM上求出sg函数,先手必胜当且仅当两个自动机上的节点的sg函数值不同。对第一个SAM的每个节点求出它在第二个SAM上对应的合法的路径条数【注意是路径条数,不是节点个数】,然后按照求字典序k小的方法dfs就可以了。
注意几个细节:
1.后继状态总数会爆long long,但是显然总数只需要存到<script type="math/tex" id="MathJax-Element-21">k</script>。
2.一个点可能有好几条字母相同的出边,也就是其实给的不是trie。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const 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);
}