codeforces568C. New Language
传送门:http://codeforces.com/problemset/problem/568/C
思路:贪心+2-sat判定
先判定原串是否合法,合法就输出原串。
否则贪心地从大到小枚举lcp,用2-sat判定
求出最长的lcp后,对于后面每一位,分别贪心尝试最小的元辅音,先试字典序小的,用2-sat判定即可
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
const int maxn=405,maxm=maxn*maxn*5;
using namespace std;
int n,b[maxm][4],a[maxn],next[maxn][2],ans[maxn],m;
char s[maxn];bool ch[maxn],allc,allv;//V:1 C:0
int P(int x,int op){return op*n+x;}//V 2*(n-1)+1 C 2*(n-1) sb错误毁一生....n要-1,不然会有2*n+1的点
struct twosat{
int frm[maxm],pre[maxm],now[maxn],son[maxm],tot,tim,dfn[maxn],low[maxn],bel[maxn],bcnt,last,top,q[maxn];
bool ins[maxn];
void clear(){memset(now,0,sizeof(now)),tot=0;}
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b,frm[tot]=a;}
void add(int a,int b,int c,int d){add(P(a,b),P(c,d));}
void del(){now[frm[tot]]=pre[tot],tot--;}
void tarjan(int x){
dfn[x]=low[x]=++tim,q[++top]=x,ins[x]=1;
for (int y=now[x];y;y=pre[y]){
if (!dfn[son[y]]) tarjan(son[y]),low[x]=min(low[x],low[son[y]]);
else if (ins[son[y]]) low[x]=min(low[x],dfn[son[y]]);
}
if (dfn[x]==low[x]){
bcnt++;int xx;
do{
xx=q[top--],bel[xx]=bcnt,ins[xx]=0;
}while (x!=xx);
}
}
bool check(){
memset(ins,0,sizeof(ins));
memset(dfn,0,sizeof(dfn)),tim=bcnt=top=0;
for(int i=1;i<=(n<<1);++i) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;++i) if(bel[P(i,0)]==bel[P(i,1)]) return 0;
return 1;
}
}T;
bool init(){
scanf("%s",s);memset(next,-1,sizeof(next));
for (int i=strlen(s)-1;i>=0;i--){
ch[i]=(s[i]=='V'),allc&=(s[i]=='C'),allv&=(s[i]=='V');
next[i][ch[i]]=i,next[i][ch[i]^1]=next[i+1][ch[i]^1];
}
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
int x,y;char s1[2],s2[2];
scanf("%d%s%d%s",&x,s1,&y,s2);
b[i][0]=x,b[i][1]=(s1[0]=='V');
b[i][2]=y,b[i][3]=(s2[0]=='V');
}
scanf("%s",s+1);
for (int i=1;i<=n;i++) a[i]=s[i]-'a';
bool can=1;
for (int i=1;i<=m;i++)
if (ch[a[b[i][0]]]==b[i][1]&&ch[a[b[i][2]]]!=b[i][3]){can=0;break;}
if (can) printf("%s\n",s+1);
return can;
}
void work(){
int k=n,typek=-1;
T.clear();
for (int i=1;i<=m;i++){
T.add(P(b[i][0],b[i][1]),P(b[i][2],b[i][3]));
T.add(P(b[i][2],b[i][3]^1),P(b[i][0],!b[i][1]));
}
for(int i=1;i<=n;++i){
if(next[0][0]==-1) T.add(P(i,0),P(i,1));//只有一种可选,那每位都只有一种选法
if(next[0][1]==-1) T.add(P(i,1),P(i,0));
}
for (k=n;k;k--){//第一个与原串不同的位置 ,从大到小贪心枚举lcp
for (int i=1;i<k;i++) T.add(P(i,!s[i]=='V'),P(i,s[i]=='V'));
int t[5],cnt=0,nowc=a[k];
for (int pp=0;pp<=1;pp++)//枚举两种种类
if (next[nowc+1][pp]!=-1) t[++cnt]=next[nowc+1][pp];
sort(t+1,t+1+cnt);//优先选字典序小的
for (int i=1;i<=cnt;i++){
for (int j=1;j<k;j++)
T.add(P(j,!ch[a[j]]),P(j,ch[a[j]]));
T.add(P(k,!ch[t[i]]),P(k,ch[t[i]]));
bool can=T.check();
for (int j=1;j<=k;j++)T.del();
if (can){typek=ch[t[i]];break;}
}
if (typek!=-1) break;
}
if (!k){puts("-1");return;}
for (int i=1;i<k;i++) ans[i]=a[i];
ans[k]=next[a[k]+1][typek];//确定第k位
for (++k;k<=n;k++){
int t=0;
if (next[0][0]==-1||(next[0][1]!=-1&&next[0][0]>next[0][1])) t=1;//因为第k位已保证新串大于原串,所以后面只要枚举最小元辅音,优先选小的。
for (int i=1;i<k;i++) T.add(P(i,!ch[ans[i]]),P(i,ch[ans[i]]));
T.add(P(k,!t),P(k,t));
if (T.check()) ans[k]=next[0][t];else ans[k]=next[0][t^1];
for (int i=1;i<=k;i++) T.del();
}
for (int i=1;i<=n;i++) putchar(ans[i]+'a');puts("");
}
int main(){if (!init()) work();return 0;}
/*
VCV
10 10
1 V 7 V
7 V 1 V
10 V 8 C
9 V 1 V
6 C 2 C
1 V 9 V
2 C 5 C
2 V 9 C
2 C 10 C
1 V 4 C
bbbbcaabab
ans:bbbcbababb
*/

浙公网安备 33010602011771号