【TCO 2012】2A EvenPaths
2015-09-29 10:44
393 查看
2A EvenPaths
Description
给出一张拓扑图,其中有一些点可能有障碍,这样的所有可能局面中,从0到1的不同路径有偶数条的局面有多少种。
Difficulty
★★★★★
Main Algorithm
分段(meet in middle)DP
快速沃尔士变换
Complexity
Solution
由于有个局面,所以需要用一些方法来处理。
我们将拓扑图按一个割分成两部分,每一部分都有
个障碍。
对于一个部分,可以暴力
枚举后DP出从0/1到它的方案数。
这样,关键就是将两半的答案合并。
我们将与0相连的点的贡献(即往连向1的点上应有的转移)处理为一个向量,其中i的权值表示在1集合中障碍点的状态压位后为i的由0集合贡献的方案数。
另一个向量为从1集合对应点到1号点的方案数。
假如两半局面合并了,合法方案为那些临界点的方案数和为偶数的方案。故需要一个合并方法。
可以发现,对应点的合并的结果是将前一半的结果与后一半逻辑与起来。那么其实我们是要算一个卷积:
类似于SRM 518 NIM,我们构造一个变换来加速这个卷积。
由于逻辑与运算是位独立的,不妨设
.
那么
.
可以验证这个变换是可以满足上述卷积的。
其逆显然为
.
故只需要用这个变换就能在
的时间内解决卷积了。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> #include <vector> #include <cmath> #define Rep(i, x, y) for (int i = x; i <= y; i ++) #define Dwn(i, x, y) for (int i = x; i >= y; i --) #define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex) #define RepG(i, x) for(int i = pos2[x]; i; i = g2[i].nex) using namespace std; typedef long long LL; const int M = 300005, N = 53; struct Edge { int y, nex; } g[N * N * 2], g2[N * N * 2]; LL ans, p[M], q[M]; int n, m, in , in2 , sz, sz2, pos , pos2 , In , In2 , que , hd, tl, n1, n0, num; int d , f , b , f0 , c , a1 ; class EvenPaths { public: void Init(int x, int y) { g[++ sz] = (Edge) { y, pos[x] }, pos[x] = sz; In[y] ++; } void Init2(int x, int y) { g2[++ sz2] = (Edge) { y, pos2[x] }, pos2[x] = sz2; In2[y] ++; } void Topo() { que[hd = tl = 1] = 0, f[0] = 1, in[0] = In[0] = n + 10; Rep(i, 1, n) { in[i] = In[i]; if (!in[i]) que[++ tl] = i, f[i] = 0; } while (hd <= tl) { int x = que[hd ++]; RepE(i, x) { int y = g[i].y; in[y] --; if (!in[y]) { if (b[y] && num < 16) c[y] = 1, num ++; que[++ tl] = y; } } } } void Build() { n0 = (1 << num) - 1; Rep(i, 0, n) if (b[i] && !c[i]) a1[++ n1] = i; Rep(i, 0, n0) { int k = i; Rep(j, 0, n) if (c[j]) { d[j] = k % 2, k >>= 1; } memset(f, 0, sizeof(f)); que[hd = tl = 1] = 0, f[0] = 1; Rep(j, 0, n) { in[j] = In[j]; if (!in[j]) que[++ tl] = j; } while (hd <= tl) { int x = que[hd ++]; if ((b[x] && !c[x]) || d[x]) { RepE(j, x) { int y = g[j].y; in[y] --; if (!in[y]) que[++ tl] = y; } continue ; } RepE(j, x) { int y = g[j].y; in[y] --; f[y] ^= f[x]; if (!in[y]) que[++ tl] = y; } } k = 0; Rep(j, 1, n1) if (f[ a1[j] ]) k += (1 << (j-1)); if (f[1]) k += 1 << n1; p[k] ++; } m = (1 << (n1 + 1)) - 1; Rep(i, 0, (1 << n1) - 1) { int k = i; Rep(j, 1, n1) { d[ a1[j] ] = k % 2, k >>= 1; } memset(f0, 0, sizeof(f0)); que[hd = tl = 1] = 1, f0[1] = 1, in[1] = n + 1, in[0] = In2[0]; Rep(j, 2, n) { in[j] = In2[j]; if (!in[j]) que[++ tl] = j; } while (hd <= tl) { int x = que[hd ++]; if (d[x]) { RepG(j, x) { int y = g2[j].y; in[y] --; if (!in[y]) que[++ tl] = y; } continue ; } RepG(j, x) { int y = g2[j].y; in[y] --; f0[y] ^= f0[x]; if (!in[y]) que[++ tl] = y; } } k = 1 << n1; Rep(j, 1, n1) if (!d[ a1[j] ] && f0[ a1[j] ]) k += (1 << (j-1)); q[k] ++; } } void FWT(LL *b, int l, int r, int v) { if (l == r) return ; int mid = (l + r) >> 1; FWT(b, l, mid, v), FWT(b, mid + 1, r, v); Rep(i, 0, mid - l) b[i + l] += b[mid + i + 1] * v; } void Calc() { FWT(p, 0, m, 1), FWT(q, 0, m, 1); Rep(i, 0, m) p[i] *= q[i]; FWT(p, 0, m, -1); Rep(i, 0, m) { int k = i, k0 = 0; while (k) k0 += (k % 2), k >>= 1; if (k0 % 2 == 0) ans += p[i]; } } long long theCount(vector <string> maze, string rooms) { n = maze.size() - 1; Rep(i, 0, n) { Rep(j, 0, n) if (maze[i][j] == 'Y') Init(i, j), Init2(j, i); b[i] = (rooms[i] == '?'); } Topo(); Build(), Calc(); return ans; } };