bzoj3127/3697 [Usaco2013 Open]Yin and Yang
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3127
http://www.lydsy.com/JudgeOnline/problem.php?id=3697
【题解】
点分治。
f[i,0/1]表示前面一坨路径和为i,是否存在休息站。
分类讨论:休息站在点分的地方,休息站在前面子树,休息站在当前子树
子树合并的时候顺便算一发贡献即可。
# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10; const int mod = 1e9+7; # define RG register # define ST static int n; ll ans = 0; int head[M], nxt[M], to[M], w[M], tot=0; bool vis[M]; inline void add(int u, int v, int _w) { ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v; w[tot] = _w; } inline void adde(int u, int v, int _w) { add(u, v, _w); add(v, u, _w); } int sz[M], mx[M]; inline void dfsSize(int x, int fa = 0) { sz[x] = 1; mx[x] = 0; for (int i=head[x]; i; i=nxt[i]) { if(to[i] == fa || vis[to[i]]) continue; dfsSize(to[i], x); sz[x] += sz[to[i]]; if(sz[to[i]] > mx[x]) mx[x] = sz[to[i]]; } } int centre = 0, mi; inline void dfsCentre(int x, int tp, int fa = 0) { if(sz[tp] - sz[x] > mx[x]) mx[x] = sz[tp] - sz[x]; if(mx[x] < mi) mi = mx[x], centre = x; for (int i=head[x]; i; i=nxt[i]) { if(to[i] == fa || vis[to[i]]) continue; dfsCentre(to[i], tp, x); } } int t[M]; ll f[M][2], g[M][2]; int mxd, vmin = 1e9, vmax = 0; // g: cur tree, f: all inline void dfsAns(int x, int v, int fa) { if(t[v]) g[v][1] ++; else g[v][0] ++; t[v] ++; vmin = min(vmin, v), vmax = max(vmax, v); for (int i=head[x]; i; i=nxt[i]) { if(to[i] == fa || vis[to[i]]) continue; dfsAns(to[i], v+w[i], x); } t[v] --; } inline void calcAns(int x) { int Vmin = 1e7, Vmax = 0; f[n][0] = 1; for (int i=head[x]; i; i=nxt[i]) { if(vis[to[i]]) continue; vmin = 1e7, vmax = 0; dfsAns(to[i], n+w[i], x); Vmin = min(Vmin, vmin), Vmax = max(Vmax, vmax); ans += (f[n][0]-1) * g[n][0]; //啥都不动,没有1,是没有意义的,为了计数方便,前面先赋值1. for (int j=vmin-n; j<=vmax-n; ++j) // 将g合并到f上,统计答案 ans += f[n-j][1] * g[n+j][1] + f[n-j][0] * g[n+j][1] + f[n-j][1] * g[n+j][0]; for (int j=vmin; j<=vmax; ++j) { f[j][0] += g[j][0]; f[j][1] += g[j][1]; g[j][0] = g[j][1] = 0; } } for (int j=Vmin; j<=Vmax; ++j) f[j][0] = f[j][1] = 0; } inline void dfs(int x) { dfsSize(x); mi = n; dfsCentre(x, x); // cross the centre calcAns(centre); // ================ vis[centre] = 1; for (int i=head[centre]; i; i=nxt[i]) { if(!vis[to[i]]) dfs(to[i]); } } int main() { scanf("%d", &n); for (int i=1; i<n; ++i) { int u, v, _w; scanf("%d%d%d", &u, &v, &_w); adde(u, v, _w ? 1 : -1); } dfs(1); printf("%lld\n", ans); return 0; }
把0/1记录1/-1会方便很多啊不过注意下标+n