[NOI2017]游戏
经典2-sat。
题意:有n个变量,每个变量有三个取值。但是有的变量的一个取值被限制为不能取。
只有不超过8个变量的取值未被限制,即三个都能取。
有m个约束条件,形如:若第i个变量取了a则第j个变量必须取b。
输出一种取值方案。无解输出-1。
n<=50000,m<=100000
好,假装是3-sat的东西.......
一个朴素想法是枚举未被限制的变量的取值,150000*3^8,超时。
然而正解就这样从TLE中浮出水面:假设未被限制的变量都被限制了,那么枚举限制了哪一个,两次即可覆盖所有情况。
时间复杂度150000*2^8,AC。
(在UOJ上面会97)
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 5 const int N = 50010; 6 7 struct Edge { 8 int nex, v; 9 }edge[N << 2]; int top; 10 11 struct ORDER { 12 int a, b, c, d; 13 }ask[N << 1]; 14 15 int e[N * 3], dfn[N * 3], low[N * 3], tot, stk[N * 3], t, space[10], sp; 16 int scc_cnt, fr[N * 3], use[N], m, n; 17 bool in_stk[N * 3]; 18 char s[N], bb[2], dd[2]; 19 20 inline void add(int x, int y) { 21 top++; 22 edge[top].nex = e[x]; 23 edge[top].v = y; 24 e[x] = top; 25 return; 26 } 27 28 void tarjan(int x) { 29 dfn[x] = low[x] = ++tot; 30 in_stk[x] = 1; 31 stk[++t] = x; 32 for(int i = e[x]; i; i = edge[i].nex) { 33 int y = edge[i].v; 34 if(!dfn[y]) { 35 tarjan(y); 36 low[x] = std::min(low[x], low[y]); 37 } 38 else if(in_stk[y]) { 39 low[x] = std::min(low[x], dfn[y]); 40 } 41 } 42 if(low[x] == dfn[x]) { 43 scc_cnt++; 44 int y; 45 do { 46 y = stk[t--]; 47 in_stk[y] = 0; 48 fr[y] = scc_cnt; 49 } while(y != x); 50 } 51 return; 52 } 53 54 inline int _(int x) { // opposite 55 int i = x; 56 while(i > n) { 57 i -= n; 58 } 59 for(int j = 0; j < 3; j++) { 60 if(i + j * n != x && use[i] != j + 1) { 61 return i + j * n; 62 } 63 } 64 return -0x3f3f3f3f; 65 } 66 67 inline void out() { 68 for(int i = 1; i <= n; i++) { 69 int x = use[i] == 1 ? i + n : i; 70 int y = _(x); 71 x = (fr[x] < fr[y]) ? x : y; 72 putchar('A' + ((x - 1) / n)); 73 } 74 return; 75 } 76 77 inline bool solve() { 78 for(int i = 1; i <= m; i++) { // a.b -> c.d 79 int x = ask[i].a + (ask[i].b - 1) * n; 80 int y = ask[i].c + (ask[i].d - 1) * n; 81 if(use[ask[i].a] == ask[i].b) { 82 continue; 83 } 84 if(use[ask[i].c] == ask[i].d) { 85 add(x, _(x)); 86 } 87 else { 88 add(x, y); 89 add(_(y), _(x)); 90 } 91 } 92 93 for(int i = 1; i <= n; i++) { 94 for(int j = 1; j <= 3; j++) { 95 if(use[i] != j && !dfn[i + (j - 1) * n]) { 96 tarjan(i + (j - 1) * n); 97 } 98 } 99 } 100 101 for(int i = 1; i <= n; i++) { 102 int x = use[i] == 1 ? i + n : i; 103 int y = _(x); 104 if(fr[x] == fr[y]) { 105 return 0; 106 } 107 } 108 return 1; 109 } 110 111 inline void clear() { 112 top = 0; 113 tot = 0; 114 scc_cnt = 0; 115 memset(dfn, 0, sizeof(dfn)); 116 memset(low, 0, sizeof(low)); 117 memset(e, 0, sizeof(e)); 118 return; 119 } 120 121 int main() { 122 int d; 123 scanf("%d%d", &n, &d); 124 scanf("%s", s + 1); 125 for(int i = 1; i <= n; i++) { 126 if(s[i] == 'x') { 127 space[++sp] = i; 128 } 129 else { 130 use[i] = (s[i] - 'a') + 1; 131 } 132 } 133 scanf("%d", &m); 134 for(int i = 1; i <= m; i++) { 135 scanf("%d%s%d%s", &ask[i].a, bb, &ask[i].c, dd); 136 ask[i].b = bb[0] - 'A' + 1; 137 ask[i].d = dd[0] - 'A' + 1; 138 } 139 140 int lm = (1 << d); 141 for(int i = 0; i < lm; i++) { 142 if(i) { 143 clear(); 144 } 145 for(int j = 0; j < d; j++) { 146 use[space[j + 1]] = ((i >> j) & 1) + 1; 147 } 148 if(solve()) { 149 out(); 150 return 0; 151 } 152 } 153 printf("-1"); 154 return 0; 155 }