[NOI2017]游戏 2-sat
题解:
首先观察到,如果没有x的话,这就是一个2-sat问题。
建图方式:对于限制d1 c1 d2 c2,其中d1, d2分别代表比赛编号,c1, c2代表出场的赛车。
1,如果d1不能选c1,那么该限制是不会起到作用的,所以不连边。
2,否则如果d2不能选c2,那么意味这d1-c1不能被选,所以连d1-c1 --- > d1-c2的边,表示必须取d1-c2。
3,否则都可以选,所以连d1-c1 ---> d2-c2 , d1-c2 ---> d2-c1.
跑tarjan求强连通分量即可。
那么有x应该怎么做?
观察到对于一组数据,x最多有8场,所以3^8枚举每场x的比赛是abc中的哪种(不选哪辆车),然后在跑2-sat即可。
这里输出方案可以不用反向建边+拓扑排序,因为栈是后进先出,所以先出栈的点刚好是反向建边+拓扑排序后先遇到的点,因此只需要记录每个点所在的联通块编号,然后对于每场比赛选择联通块编号小的方案输出即可。
题目数据极水,A了也不一定是正确代码,请特别注意检查代码正确性。(如果你发现我有哪里错了也可以告诉我QAQ)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 300100 5 #define ac 1000000 6 7 int n, d, timer, all, m; 8 int s[AC], dfn[AC], low[ac], belong[ac], d1[AC], c1[AC], d2[AC], c2[AC]; 9 int sta[AC], top; 10 int Head[AC], Next[ac], date[ac], tot; 11 char ss[AC]; 12 bool flag, z[AC]; 13 14 inline int read() 15 { 16 int x = 0;char c = getchar(); 17 while(c > '9' || c < '0') c = getchar(); 18 while(c >= '0' && c <= '9') x = 10 * x + c - '0', c = getchar(); 19 return x; 20 } 21 22 inline void upmin(int &a, int b) 23 { 24 if(b < a) a = b; 25 } 26 27 inline void add(int f, int w) 28 { 29 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot; 30 //printf("%d ---> %d\n", f, w); 31 } 32 33 void tarjan(int x) 34 { 35 int now; 36 dfn[x] = low[x] = ++ timer, sta[++top] = x, z[x] = true; 37 for(R i = Head[x]; i; i = Next[i]) 38 { 39 now = date[i]; 40 if(!dfn[now]) 41 { 42 tarjan(now); 43 upmin(low[x], low[now]); 44 } 45 else if(z[now]) upmin(low[x], low[now]);//已经出栈的无需考虑 46 } 47 if(low[x] == dfn[x]) 48 { 49 ++all; 50 while(1) 51 { 52 now = sta[top--], belong[now] = all, z[now] = false;//标记出栈 53 if(now == x) break; 54 } 55 } 56 } 57 58 void init() 59 { 60 tot = all = timer = 0; 61 memset(Head, 0, sizeof(Head)); 62 memset(dfn, 0, sizeof(dfn)); 63 memset(z, 0, sizeof(z)); 64 } 65 66 inline int get(int x, int color) 67 { 68 if(s[x] == color) return 0;//不合法 69 if(s[x] == 1) return x * 2 + color - 2; 70 else if(s[x] == 2) return x * 2 + ((color == 1) ? 0 : 1); 71 else return x * 2 + color - 1; 72 } 73 74 void build() 75 { 76 init(); 77 for(R i = 1; i <= m; i ++) 78 { 79 int x = get(d1[i], c1[i]), y = get(d2[i], c2[i]); 80 if(x) 81 { 82 if(y) add(x, y), add(y ^ 1, x ^ 1); 83 else add(x, x ^ 1); 84 } 85 } 86 int b = n * 2 + 1; 87 for(R i = 2; i <= b; i ++) 88 if(!dfn[i]) tarjan(i); 89 for(R i = 2; i <= b; i += 2)//error !!!到b 90 if(belong[i] == belong[i ^ 1]) return ; 91 for(R i = 1; i <= n; i ++) 92 { 93 int x = i * 2; 94 if(s[i] == 1) putchar(belong[x] < belong[x ^ 1] ? 'B' : 'C'); 95 else if(s[i] == 2) putchar(belong[x] < belong[x ^ 1] ? 'A' : 'C'); 96 else putchar(belong[x] < belong[x ^ 1] ? 'A' : 'B'); 97 } 98 flag = true; 99 } 100 101 void pre() 102 { 103 char ch; 104 n = read(), d = read(); 105 scanf("%s", ss + 1); 106 for(R i = 1; i <= n; i ++) 107 if(ss[i] != 'x') s[i] = ss[i] - 'a' + 1; 108 m = read(); 109 for(R i = 1; i <= m; i ++) 110 { 111 d1[i] = read(), cin >> ch, c1[i] = ch - 'A' + 1; 112 d2[i] = read(), cin >> ch, c2[i] = ch - 'A' + 1; 113 } 114 } 115 116 void dfs(int x) 117 { 118 if(flag) return ; 119 if(x > n) 120 { 121 build(); 122 return ; 123 } 124 if(!s[x]) 125 { 126 s[x] = 1, dfs(x + 1); 127 s[x] = 2, dfs(x + 1); 128 s[x] = 3, dfs(x + 1); 129 s[x] = 0; 130 } 131 else dfs(x + 1); 132 } 133 134 int main() 135 { 136 freopen("in.in","r",stdin); 137 pre(); 138 dfs(1); 139 if(!flag) printf("-1"); 140 fclose(stdin); 141 return 0; 142 }