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



posted @ 2015-10-21 21:47  orzpps  阅读(318)  评论(0编辑  收藏  举报