CF875C National Property 题解

Codeforces
Luogu

Description.

给定一些字符串,你需要给他们确定大小写,使得字符串字典序递增。  

Solution.

首先很显然,递增只需要 \(n-1\) 对相邻字符串都满足 \(S_{i-1}<S_{i}\)
那么我们比较相邻字符串,发现必定有一段两个字符串相同。
我们分类讨论下一位:

  1. 下一位在第一个字符串中不存在——必定成立
  2. 下一位在第二个字符串中不存在——必定无解
  3. 下一位在两个字符串中都存在——比较复杂,是后文重点解决的问题。

那么我们现在需要考虑如何计算第三类的答案。
刚开始觉得好像只需要一个拓扑排序就好了,然后怒斥是大水题,然后发现自己假地很惨。
没想到 2-sat 问题,应该是没想的很深
如果想到了 2-sat,那这题就做完了。
就考虑分类讨论:

  1. \(a<b\),那 \(a\) 取小写能推出 \(b\) 取小写,\(b\) 取大写能推出 \(a\) 取大写。
  2. \(a>b\),那只可能是 \(a\) 取大写,\(b\) 取小写。

然后套上 2-sat 模板即可。

Coding.

点击查看垃圾代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}/*}}}*/
struct edge{int to,nxt;}e[400005];int n,m,et,head[200005];
int buf[200005],*a[100005],ln[100005],tt,st[200005],tp;
int dfn[200005],dt,low[200005],cl[200005],ct;char v[200005];
inline void adde(int x,int y) {e[++et]=(edge){y,head[x]},head[x]=et;}
inline void add(int x,int y)
{//x表示大写,x+m表示小写,x<y
	if(x<y) adde(x+m,y+m),adde(y,x);
	if(x>y) adde(x+m,x),adde(y,y+m);
}
inline void tarjan(int x)
{
	dfn[x]=low[x]=++dt,v[x]=1,st[++tp]=x;
	for(int i=head[x];i;i=e[i].nxt)
		if(!dfn[e[i].to]) tarjan(e[i].to),low[x]=min(low[e[i].to],low[x]);
		else if(v[e[i].to]) low[x]=min(dfn[e[i].to],low[x]);
	if(dfn[x]==low[x]) {int y=++ct;do v[y=st[tp--]]=0,cl[y]=ct;while(y^x);}
}
int main()
{
	read(n),read(m),a[1]=buf;for(int i=1;i<=n;i++)
	{
		read(ln[i]),a[i+1]=a[i]+ln[i];
		for(int j=1;j<=ln[i];j++) read(a[i][j]);
	}
	for(int i=2;i<=n;i++)
	{
		char fg=1;for(int j=1;j<=ln[i]&&j<=ln[i-1]&&fg;j++)
			if(a[i][j]^a[i-1][j]) add(a[i-1][j],a[i][j]),fg=0;
		if(fg&&ln[i]<ln[i-1]) return puts("No"),0;
	}
	for(int i=1;i<=m+m;i++) if(!dfn[i]) tarjan(i);
	for(int i=1;i<=m;i++) if(cl[i]==cl[i+m]) return puts("No"),0;
	int cnt=0;puts("Yes");for(int i=1;i<=m;i++) if(cl[i]<cl[i+m]) cnt++;
	printf("%d\n",cnt);for(int i=1;i<=m;i++) if(cl[i]<cl[i+m]) printf("%d ",i);
	return putchar('\n'),0;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15012727.html