CF875C National Property
题目描述
给定一些字符串,其中字母用数字表示,并且初始是小写的。你可以把一些小写字母改成大写,但同时你要把所有同种字母全部改成大写。问是否能经过一些操作使得最终的字符串序列满足按字典序升序排列。如果能,则需要输出方案。
题解
可以把每个字母看成点,有大写和小写俩种情况。若对于俩个相邻的字符串来说,字典序大小的比较其实是第一个不相同的字符,找到那个不相同的位置,可能不存在,假设存在设为x,若i的字典序大于i - 1的,则这俩行的x位置的大小写情况应该相同。若i-1的字典序大于i的,则说明必然是i-1在x的字符是大写,在i的x字符必然是小写,这题其实看似像扩展域并查集,但在并查集上无法体现&关系,只能2-SAT。
1 #include <bits/stdc++.h> 2 #define MAX 200005 3 using namespace std; 4 5 int n, m, cnt, tot; 6 int len, lst[MAX], a[MAX], col[MAX]; 7 int head[MAX], Next[MAX*2], vet[MAX*2]; 8 9 void add(int x, int y){ 10 cnt++; 11 Next[cnt] = head[x]; 12 head[x] = cnt; 13 vet[cnt] = y; 14 } 15 16 int low[MAX], dfn[MAX], vis[MAX], T; 17 stack<int> s; 18 void tarjan(int x){ 19 low[x] = dfn[x] = ++T; 20 vis[x] = true; 21 s.push(x); 22 for(int i = head[x]; i; i = Next[i]){ 23 int v = vet[i]; 24 if(!dfn[v]){ 25 tarjan(v); 26 low[x] = min(low[x], low[v]); 27 } 28 else if(vis[v]){ 29 low[x] = min(low[x], dfn[v]); 30 } 31 } 32 if(low[x] == dfn[x]){ 33 tot++; 34 int t = -1; 35 while(t != x){ 36 t = s.top(); 37 s.pop(); 38 vis[t] = false; 39 col[t] = tot; 40 } 41 } 42 } 43 44 int main() 45 { 46 cin >> m >> n; 47 for(int i = 1; i <= m; i++){ 48 int t; 49 scanf("%d", &t); 50 for(int j = 1; j <= t; j++){ 51 scanf("%d", &a[j]); 52 } 53 for(int j = 1; j <= min(len, t); j++){ 54 if(lst[j] < a[j]){ 55 add(a[j] + n, lst[j] + n); 56 add(lst[j], a[j]); 57 break; 58 } 59 else if(lst[j] > a[j]){ 60 add(lst[j], lst[j] + n); 61 add(a[j] + n, a[j]); 62 break; 63 } 64 if(j == min(len, t) && len > t){ 65 puts("No"); 66 return 0; 67 } 68 } 69 len = t; 70 for(int j = 1; j <= t; j++) lst[j] = a[j]; 71 } 72 73 for(int i = 1; i <= n*2; i++){ 74 if(!dfn[i]) tarjan(i); 75 } 76 for(int i = 1; i <= n; i++){ 77 if(col[i] == col[i+n]){ 78 puts("No"); 79 return 0; 80 } 81 } 82 83 puts("Yes"); 84 int ans = 0; 85 for(int i = 1; i <= n; i++){ 86 if(col[i] > col[i+n]){ 87 ans++; 88 } 89 } 90 cout << ans << endl; 91 for(int i = 1; i <= n; i++){ 92 if(col[i] > col[i+n]){ 93 printf("%d ", i); 94 } 95 } 96 97 return 0; 98 }