Noi2011部分题解
道路修建:
简直是送分题。。。
先算出$f_x$表示以$x$为根的子树大小,然后每条边$(x,y)$的贡献显然就是$|n - f_y - f_y|$
$dfs$一遍即可。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 inline char Getchar() 5 { 6 static char buf[1000010], *p1 = buf, *p2 = buf; 7 return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++; 8 } 9 10 inline int Read() 11 { 12 int x = 0, f = 1; char s = Getchar(); 13 for(; !isdigit(s); s = Getchar()) if(s == '-') f = -1; 14 for(; isdigit(s); s = Getchar()) x = x * 10 + s - 48; 15 return x * f; 16 } 17 18 const int MAXN = 1000010; 19 20 int n, f[MAXN]; long long ans; 21 int tot, Head[MAXN], ver[MAXN << 1], Next[MAXN << 1], edge[MAXN << 1]; 22 23 void add(int u, int v, int z) 24 { 25 ver[++tot] = v; 26 edge[tot] = z; 27 Next[tot] = Head[u]; 28 Head[u] = tot; 29 } 30 31 void dfs(int x, int fa) 32 { 33 f[x] = 1; 34 for(int i = Head[x]; i; i = Next[i]) 35 { 36 int y = ver[i]; 37 if(y == fa) continue; 38 dfs(y, x); 39 f[x] += f[y]; 40 ans += 1ll * edge[i] * abs(n - f[y] - f[y]); 41 } 42 } 43 44 int main() 45 { 46 n = Read(); 47 for(int i = 1; i < n; ++i) 48 { 49 int u = Read(), v = Read(), z = Read(); 50 add(u, v, z); 51 add(v, u, z); 52 } 53 dfs(1, 0); 54 printf("%lld\n", ans); 55 return 0; 56 }
手玩几组数据可以发现,空格所经过的位置是不会重复的,而且这些位置都是白黑交错的,这让我们联想到二分图的增广路。
将起点和黑点设为左部点,白点设为右部点,那么每步操作就相当于是从一边走到另一边,且不会走到之前走过的点。
然后我们考虑什么情况下先手必败,如果当前的二分图存在一个最大匹配,且不包含起点,那么先手必败。
为什么呢,因为先手从起点出发,随便走一条非匹配边,然后后手一定可以找到一条匹配边,直到先手无路可走。
如果后手无法找到一条匹配边继续走的话,那么之前的路径就是一条增广路(非匹配边开始,非匹配边结束),这显然不符合之前最大匹配的条件。
那怎么判断存不存在一组不包含起点的最大匹配呢,我们强制当前点不选,如果它的匹配点仍然能找到新的匹配点,就说明有一种最大匹配不包含它。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 #define Get(i, j) ((i - 1) * m + j) 5 const int dx[4] = {-1, 1, 0, 0}; 6 const int dy[4] = {0, 0, -1, 1}; 7 const int MAXN = 2010; 8 const int MAXM = 10010; 9 10 int n, m, q, sx, sy, win[MAXN], mp[50][50]; 11 int match[MAXN], visit[MAXN], ban[MAXN]; 12 int tot, Head[MAXN], ver[MAXM], Next[MAXM]; 13 int cnt, ans[MAXN]; 14 15 void add(int u, int v) 16 { 17 ver[++tot] = v; 18 Next[tot] = Head[u]; 19 Head[u] = tot; 20 } 21 22 int dfs(int x) 23 { 24 if(ban[x]) return 0; 25 for(int i = Head[x]; i; i = Next[i]) 26 { 27 int y = ver[i]; 28 if(ban[y]) continue; 29 if(!visit[y]) 30 { 31 visit[y] = 1; 32 if(!match[y] || dfs(match[y])) 33 { 34 match[y] = x; 35 match[x] = y; 36 return 1; 37 } 38 } 39 } 40 return 0; 41 } 42 43 int main() 44 { 45 char str[50]; 46 scanf("%d %d", &n, &m); 47 for(int i = 1; i <= n; ++i) 48 { 49 scanf("%s", str + 1); 50 for(int j = 1; j <= m; ++j) 51 { 52 if(str[j] == 'X') mp[i][j] = 1; 53 else if(str[j] == 'O') mp[i][j] = 0; 54 else 55 { 56 mp[i][j] = 1; 57 sx = i, sy = j; 58 } 59 } 60 } 61 for(int i = 1; i <= n; ++i) 62 for(int j = 1; j <= m; ++j) 63 for(int k = 0; k < 4; ++k) 64 { 65 int x = i + dx[k], y = j + dy[k]; 66 if(x >= 1 && x <= n && y >= 1 && y <= m && mp[i][j] != mp[x][y]) 67 add(Get(i, j), Get(x, y)); 68 } 69 for(int i = 1; i <= n; ++i) 70 for(int j = 1; j <= m; ++j) 71 if(mp[i][j]) 72 { 73 memset(visit, 0, sizeof(visit)); 74 dfs(Get(i, j)); 75 } 76 scanf("%d", &q); 77 for(int i = 1; i <= (q << 1); ++i) 78 { 79 int u = Get(sx, sy); 80 ban[u] = 1; 81 if(match[u]) 82 { 83 int v = match[u]; 84 match[u] = match[v] = 0; 85 memset(visit, 0, sizeof(visit)); 86 win[i] = !dfs(v); 87 } 88 scanf("%d %d", &sx, &sy); 89 } 90 for(int i = 1; i <= q; ++i) 91 if(win[(i << 1) - 1] && win[(i << 1)]) ans[++cnt] = i; 92 printf("%d\n", cnt); 93 for(int i = 1; i <= cnt; ++i) printf("%d\n", ans[i]); 94 return 0; 95 }
先考虑暴力做法,我们把每个打印出来的串都加入AC自动机,然后对于每个询问直接匹配。
由于两个串都是AC自动机中的串,我们直接记下每个节点的父亲节点,然后从着$y$的结尾节点一直向上爬,在每个节点都跳$fail$指针,如果跳到了$x$的结尾节点就$++ans$即可。
发现对于两个相同的$y$,我们可以一次性处理,所以把询问离线下来排序处理,这样可以拿到$70$分。
我们的的问题是:$y$的每个节点沿着$fail$指针往上跳是否能到达$x$。
发现每个节点都只有一个$fail$指针,那我们把$fail$指针反过来连边,就得到了所谓的$fail$树。
那我们把上述问题也反过来,就是:$x$沿着$fail$指针往下跳能到达几个$y$的节点。
我们把$y$到根节点路径上的节点权值全部设为1,那么只需要求出$x$的子树和即可。(DFS序 + 树状数组)
即使是这样,对于每个$y$我们还是要一步步往上跳(设置权值),时间复杂度依然不能满足题目的要求。
我们慢在哪?每个串重复的前缀很多,对于离根节点比较近的那些点,我们重复设置了很多遍权值,从而严重降低了效率。
不如直接对$Trie$树进行一遍$DFS$,访问到的时候打一个+1,结束的时候打一个−1。
当一个点刚刚进入栈的时候,显然只有它到根节点路径上的节点被打了标记,此时我们就可以直接处理与这个节点相关的询问了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 #define lowbit(x) (x & (-x)) 5 const int MAXN = 100010; 6 7 char str[MAXN]; 8 int n, m, tim, ids[MAXN], low[MAXN], dfn[MAXN]; 9 int tot, Head[MAXN], ver[MAXN], Next[MAXN]; 10 int ql[MAXN], qr[MAXN], ans[MAXN]; 11 12 void add(int u, int v) 13 { 14 ver[++tot] = v; 15 Next[tot] = Head[u]; 16 Head[u] = tot; 17 } 18 19 void dfs(int u) 20 { 21 dfn[u] = ++tim; 22 for(int i = Head[u]; i; i = Next[i]) dfs(ver[i]); 23 low[u] = tim; 24 } 25 26 struct Query 27 { 28 int x, y, id, ans; 29 bool operator < (const Query &a) const 30 { 31 return y < a.y; 32 } 33 }q[MAXN]; 34 35 struct Bit 36 { 37 int sum[MAXN], len; 38 39 void add(int pos, int val) 40 { 41 for(; pos <= len; pos += lowbit(pos)) 42 sum[pos] += val; 43 } 44 45 int query(int pos) 46 { 47 int ans = 0; 48 for(; pos; pos -= lowbit(pos)) ans += sum[pos]; 49 return ans; 50 } 51 }bit; 52 53 struct AC 54 { 55 struct Node 56 { 57 int son[26], vis[26]; 58 int Next, fa, id; 59 }t[MAXN]; int cnt; 60 61 void add(char *s) 62 { 63 cnt = 1; int u = 1; 64 int len = strlen(s + 1); 65 for(int i = 1; i <= len; ++i) 66 { 67 if(s[i] >= 'a' && s[i] <= 'z') 68 { 69 if(!t[u].son[s[i] - 'a']) t[u].son[s[i] - 'a'] = ++cnt, t[cnt].fa = u; 70 u = t[u].son[s[i] - 'a']; 71 } 72 else if(s[i] == 'B') u = t[u].fa; 73 else ids[++n] = u, t[u].id = n; 74 } 75 } 76 77 void build() 78 { 79 for(int i = 0; i <= 25; ++i) t[0].son[i] = 1; 80 queue<int> q; q.push(1); t[1].Next = 0; 81 while(!q.empty()) 82 { 83 int x = q.front(); q.pop(); 84 for(int i = 0; i <= 25; ++i) 85 { 86 if(!t[x].son[i]) t[x].son[i] = t[t[x].Next].son[i]; 87 else 88 { 89 t[t[x].son[i]].Next = t[t[x].Next].son[i]; 90 q.push(t[x].son[i]); 91 } 92 } 93 } 94 } 95 96 void dfs(int u) 97 { 98 bit.add(dfn[u], 1); 99 if(t[u].id && ql[t[u].id]) 100 for(int i = ql[t[u].id]; i <= qr[t[u].id]; ++i) 101 q[i].ans = bit.query(low[ids[q[i].x]]) - bit.query(dfn[ids[q[i].x]] - 1); 102 for(int i = 0; i <= 25; ++i) 103 if(t[u].vis[i]) dfs(t[u].vis[i]); 104 bit.add(dfn[u], -1); 105 } 106 }ac; 107 108 int main() 109 { 110 scanf("%s", str + 1); 111 ac.add(str); 112 for(int i = 1; i <= ac.cnt; ++i) 113 for(int j = 0; j <= 26; ++j) ac.t[i].vis[j] = ac.t[i].son[j]; 114 ac.build(); 115 for(int i = 2; i <= ac.cnt; ++i) add(ac.t[i].Next, i); 116 dfs(1); 117 scanf("%d", &m); 118 for(int i = 1; i <= m; ++i) 119 { 120 q[i].id = i; 121 scanf("%d %d", &q[i].x, &q[i].y); 122 } 123 sort(q + 1, q + 1 + m); 124 for(int i = 1, pos = 1; i <= m; i = pos) 125 { 126 ql[q[i].y] = i; 127 while(q[pos].y == q[i].y) ++pos; 128 qr[q[i].y] = pos - 1; 129 } 130 bit.len = tim; 131 ac.dfs(1); 132 for(int i = 1; i <= m; ++i) ans[q[i].id] = q[i].ans; 133 for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]); 134 return 0; 135 }