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 */