IOI2021集训队作业154BE Evolution in Parallel
有\(n+1\)个字符串。如果\(s_i\)为\(s_j\)的子序列,则连边\((j,i)\)。
现在要求找到两条路径,使得它们只在\(0\)处相交,并且覆盖所有点。
\(n\le 4000\)
哇哈为什么我想到了dilworth引理……
先按照长度排序,然后维护两条链的链尾,设为\(a,b\)。
如果\((i,a)\in E\)和\((i,b)\in E\)只满足一个,则直接接在后面。
如果都不满足则无解。
如果都满足,则搞一个\(c=i\),暂时把两条链合成一条链。后面接着做,如果能接在这条链后面则接;如果不能接,就把它丢到\(a\)或\(b\)后面,然后\(c\)接在另一条的后面,于是又分开为两条链。(在这个局面之后链尾分别为\(i\)和\(i-1\),并且没有其它方法使得构造后链尾不为\(i\)和\(i-1\),相当于把问题划分成前后两部分。)
using namespace std;
#include <bits/stdc++.h>
#define N 4005
void imp(){
printf("impossible\n");
exit(0);
}
int n;
char str[N][N];
int s[N][N],t[N][N][3];
int len[N];
int q[N];
bool cmpq(int x,int y){return len[x]<len[y];}
void init(int i,char str[]){
int m=len[i]=strlen(str+1);
t[i][m+1][0]=t[i][m+1][1]=t[i][m+1][2]=m+1;
for (int j=m;j>=1;--j){
s[i][j]=(str[j]=='A'?0:str[j]=='C'?1:2);
memcpy(t[i][j],t[i][j+1],sizeof t[i][j]);
t[i][j][s[i][j+1]]=j+1;
}
memcpy(t[i][0],t[i][1],sizeof t[i][0]);
t[i][0][s[i][1]]=1;
}
bool sub(int u,int v){
int x=0;
for (int i=1;i<=len[u];++i){
x=t[v][x][s[u][i]];
if (x>len[v])
return 0;
}
return 1;
}
int f[N];
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&n);
scanf("%s",str[n+1]+1),len[n+1]=strlen(str[n+1]+1);
for (int i=1;i<=n;++i)
scanf("%s",str[i]+1),len[i]=strlen(str[i]+1);
for (int i=1;i<=n;++i)
q[i]=i;
sort(q+1,q+n+1,cmpq);
init(n+1,str[n+1]);
for (int i=1;i<=n;++i)
init(i,str[q[i]]);
if (!sub(n,n+1))
imp();
f[n]=n+1;
int a=n,b=n+1,c=0;
for (int i=n-1;i>=1;--i){
if (c){
if (sub(i,i+1))
f[i]=i+1;
else{
if (sub(i,a))
f[i]=a,f[c]=b;
else if (sub(i,b))
f[i]=b,f[c]=a;
else
imp();
a=i+1,b=i;
c=0;
}
}
else{
if (sub(i,a)){
if (sub(i,b))
c=i;
else
f[i]=a,a=i;
}
else if (sub(i,b))
f[i]=b,b=i;
else
imp();
}
}
if (c)
f[c]=a;
static bool bz[N];
int cnt0=0;
for (int x=1;x<=n;x=f[x])
bz[x]=1,cnt0++;
printf("%d %d\n",cnt0,n-cnt0);
for (int i=1;i<=n;++i)
if (bz[i])
printf("%s\n",str[q[i]]+1);
for (int i=1;i<=n;++i)
if (!bz[i])
printf("%s\n",str[q[i]]+1);
return 0;
}