1. 程式人生 > 其它 >CF875C National Property (題解)

CF875C National Property (題解)

P.S.

妙妙題

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;
}