codeforces 568C New Language 题解

CF568C


一眼 2-SAT,字典序最小就贪心,然后全是细节。

根据贪心从左到右逐位考虑,虽然看起来后面对前面会有限制,但是通过 2-SAT 转成前面对后面的限制了。如果前面每一位都是顶上界的(和给定字符串相等)首选就是对应字符串的那一位,否则就是 a

但这样很可能是不合法的,也就是说后面不管怎么填也没法合法,不过这个并不难判断,有以下两种情况,假设目前判断第 (i) 位能不能填给定字符。

  • 无法满足有解:前 (i) 位决定后,后面出现既必须为 V 又必须为 C 的位
  • 无法满足字典序大于等于:前 (i) 位决定后,前面仍然顶上界,假设第 (i+1)(i+k) 位选 C 还是选 V 已经确定,而 (i+k+1) 位没确定或者这位已经是字符串以外的位置,并且这一段无论怎么选也选不出字典序比给定字符串那段大的字符串(比如 V 最大选 b(i+1) 位只能选 V,而给定字符串要求 c

第二种有点复杂

然后看最优情况下不行的话,还有什么选项可以填,会决定一个位置对后面是否有解的影响的,无非就两个。

  • (i) 位是否顶上界
  • (i) 位是 C 还是 V

四种情况枚举一下,即可,如果前面不顶上界了后面就不可能再顶了,注意字典序从小到大,我这里实现的比较复杂。

using namespace std;
typedef long long LL;

LL n,e[405][405];
void floyd(){
	for(LL k = 1;k <= n + n;k ++){
		for(LL i = 1;i <= n + n;i ++){
			for(LL j = 1;j <= n + n;j ++){
				e[i][j] |= (e[i][k] && e[k][j]);
			}
		}
	}
}

LL acv[405],tcv[405];
LL mxp[2],vc[35],nxt[35],nxu[35];
string s;
LL chk(LL pos,LL now,LL x,LL id){
	LL tmp;
	if(acv[pos] != -1 && acv[pos] != vc[now]) return 0;
	for(LL i = 1;i <= n;i ++) if(e[x][i] && e[x][i + n]) return 0;
    // 无解 1
	for(LL i = 1;i <= n;i ++){
		tcv[i] = -1;
		if(e[x][i]) tcv[i] = 0;
		if(e[x][i + n]) tcv[i] = 1;
	}
	if(!id){
		for(LL i = 1;i <= n;i ++){
			if(pos + i > n) break;
			tmp = max(tcv[pos + i],acv[pos + i]);
			if(tmp == -1) break;
			if(mxp[tmp] < s[pos + i] - 'a' + 1) return 0;
			if(mxp[tmp] > s[pos + i] - 'a' + 1) break;
		}
	} // 无解 2
	for(LL i = 1;i <= n;i ++) acv[i] = max(acv[i],tcv[i]);
    // 把限制加进总限制
	return 1;
}

LL ans[405];
int main(){
	LL len,m,l1,l2,c1,c2,fla;
	char ch1,ch2;
	cin >> s; len = s.length(); s = ' ' + s;
	for(LL i = 1;i <= len;i ++) vc[i] = (s[i] == 'C' ? 0 : 1);
	vc[len + 1] = -1;// 防止后面 mi + 1 爆字符集上限
	mxp[0] = mxp[1] = -1; for(LL i = 1;i <= len;i ++) mxp[vc[i]] = i;
	for(LL i = 1;i <= len;i ++){
		nxt[i] = 0;
		for(LL j = i + 1;j <= len;j ++){
			if(vc[i] != vc[j]){ nxt[i] = j; break; }
		}
	} // 下一个不同 C/V
	for(LL i = 1;i <= len;i ++){
		nxu[i] = 0;
		for(LL j = i + 1;j <= len;j ++){
			if(vc[i] == vc[j]){ nxu[i] = j; break; }
		}
	} // 下一个同 C/V
	
	memset(acv,-1,sizeof(acv));
	memset(tcv,-1,sizeof(tcv));
	cin >> n >> m;
	while(m --){
		cin >> l1 >> ch1 >> l2 >> ch2;
		c1 = (ch1 == 'C') ? 0 : 1;
		c2 = (ch2 == 'C') ? 0 : 1;
		e[l1 + c1 * n][l2 + c2 * n] = 1;
		e[l2 + (!c2) * n][l1 + (!c1) * n] = 1;
        // 2-SAT
	} floyd();
	
	cin >> s; s = ' ' + s;
	fla = 0;
	for(LL i = 1;i <= n;i ++){
		LL mi = fla ? 1 : s[i] - 'a' + 1;
        // 顶上界优先走,此是这位的 C/V 已经确定记为 mi
        // 这里先看 mi+1 和 mi 是同 C/V 还是不同的
        // 相同就先走同 C/V 不顶上界
        // 不同就先走不同 C/V 而且这种情况无论如何都不顶上界
		if(chk(i,mi,i + vc[mi] * n,fla)) ans[i] = mi;
		else if(vc[mi] == vc[mi + 1] && chk(i,mi + 1,i + vc[mi + 1] * n,1)) ans[i] = mi + 1,fla = 1;
		else if(nxt[mi] && chk(i,nxt[mi],i + vc[nxt[mi]] * n,1)) ans[i] = nxt[mi],fla = 1;
		else if(nxu[mi] > nxt[mi] && nxu[mi] && chk(i,nxu[mi],i + vc[nxu[mi]] * n,1)) ans[i] = nxu[mi],fla = 1;
		else{ cout << -1 << '
'; return 0; }
	}
	for(LL i = 1;i <= n;i ++) cout << (char)(ans[i] + 'a' - 1); cout << '
';
	return 0;
}
原文地址:https://www.cnblogs.com/IltzInstallBI/p/13071027.html