题目传送门:http://codeforces.com/contest/875/problem/C
题目大意:现在给你n个字符串,字符集大小为m。一开始所有字母都为小写,你可以选择几种字母,将所有字符串的该种字母变为大写。要求最后的字符串按输入顺序单调不降(大写字母的字典序小于小写字母)。字母以数字1~m的形式给出,无解输出”No”,否则输出一行”Yes”,并在第二行输出要改为大写的字母种数num,第三行输出num个用空格隔开的整数,表示答案。输出任意一种方案即可,有SPJ。 n,m,∑|S|<=105 。
题目分析:被一道傻逼题坑半天系列……
我们只考虑相邻两个字符串。如果相邻的字符串都是有序的,这n个字符串肯定都是有序的。假设相邻的两个字符串为a,b(a在b前),现在找到一个最小的x使得 a[x]≠b[x] (前提是a,b互不为前缀),那么这两个字符串的比较在第x位就结束了,所以后面的字母是没有限制的。
①:如果
a[x]<b[x]
,那么b[x]选了大写,a[x]就一定要选大写,除此之外没有限制。
②:如果
a[x]>b[x]
,a[x]就一定要选大写,b[x]一定要选小写。
现在建2*m个点,表示1~m选大小写的情况。对于情况①,让代表b[x]大写的点向代表a[x]大写的点连一条边。对于情况②,在代表a[x]大写和b[x]小写的点上打个标记,表示这两个点一定要选。然后从一定要选的点开始DFS,扩展一定要选的点。然后对于字符x,如果x大写和x小写都一定要选,则无解,否则选那个一定要选的点。如果两个都不一定要选,选x小写。因为x小写一定没有出边,选它不会对其它字符构成影响。
其实这题的构图判断方法很像2-SAT,只不过它稍微简化了一点。如果把对称边连上,再用正常的2-SAT判断也是可以的。时间复杂度 O(n) 。
By the way,关于这题我一开始想到一个分治的方法。我们可以将所有字符串的第一位取出来。如果存在一个 s[i][1]>s[i+1][1] ,那么i及以前的字符串;第一位肯定要变成大写,i+1及以后的字符串,第一位肯定要求小写。如果存在两个这样的i,无解。如果不存在这样的i,那么以后需要将s[i][1]变为大写的时候,s[1~i-1][1]也肯定要变为大写。于是可以构一个图来表示这种限制。然后对于第一位相同的字符串区间,分治下去做。时间复杂度也是线性的,只不过代码很难写……
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
struct edge
{
int obj;
edge *Next;
} e[maxn];
edge *head[maxn];
int cur=-1;
bool use[maxn];
int a[maxn];
int b[maxn];
int temp[maxn];
int n,m,ans=0;
void Read(int *p)
{
scanf("%d",&p[0]);
for (int i=1; i<=p[0]; i++) scanf("%d",&p[i]);
}
void Add(int x,int y)
{
cur++;
e[cur].obj=y;
e[cur].Next=head[x];
head[x]=e+cur;
}
void Dfs(int node)
{
if (use[node]) return;
use[node]=true;
for (edge *p=head[node]; p; p=p->Next) Dfs(p->obj);
}
int main()
{
freopen("2333.in","r",stdin);
freopen("2333.out","w",stdout);
scanf("%d%d",&n,&m);
Read(a);
for (int i=2; i<=n; i++)
{
Read(b);
int len=min(a[0],b[0]),x;
for (x=1; x<=len; x++) if (a[x]!=b[x]) break;
if (x==len+1)
if (a[0]>b[0])
{
printf("No\n");
return 0;
}
else
{
for (int i=0; i<=b[0]; i++) a[i]=b[i];
continue;
}
if (a[x]>b[x]) use[ a[x] ]=use[ m+b[x] ]=true;
else Add(b[x],a[x]);
for (int i=0; i<=b[0]; i++) a[i]=b[i];
}
for (int i=1; i<=2*m; i++) if (use[i]) use[i]=false,Dfs(i);
for (int i=1; i<=m; i++)
if ( use[i] && use[m+i] )
{
printf("No\n");
return 0;
}
for (int i=1; i<=m; i++)
if (use[i]) temp[++ans]=i;
printf("Yes\n%d\n",ans);
for (int i=1; i<=ans; i++) printf("%d ",temp[i]);
printf("\n");
return 0;
}