[BZOJ3697][[FJ2014集训]采药人的路径][点分治]
2017-02-28 11:37
323 查看
[BZOJ3697][[FJ2014集训]采药人的路径][点分治]
题目大意:
给定一棵N≤100,000的无根树,树边的权值为0,1,求树上有多少条路径中0,1的数量相等且把这条路径在某一点分成两条子路径,每条子路径中0,1的数量也相等。思路:
这题一眼就看出来要用点剖(其实是我在百度上搜的点剖题),然而并不会做,于是去黄学长的博客里学习了一发。首先为了方便,我们把权值为0的路径权值改为−1,这样一条路径上0,1数量相等可以转化为路径的权值和为0。
将树点剖以后,对于每个重心,我们只需要考虑重心子树中每个点到根的路径,一条符合题目要求的路径,肯定能分成两部分:s→g,g→t(s,t同属于重心g的子树中),其中s→g的权值和为k,g→t的权值和为−k。(满足s→t的权值和为0)。
接着考虑休息站的条件,我们可以对于两条路径分开考虑,每条子路径在dfs的过程中记录当前的权值前缀,如果在同一条路径中,权值前缀和最终权值和出现了相同 pre[i]=sum[1,n],那么[i+1,n]这一段的权值和必定为0,即i和i+1这两条边共同连接的点就是休息站。
这样我们枚举根节点(重心)的每个子树。用f[i][0…1],g[i][0…1]分别表示当前子树以及前面几个子树和为i的路径数目,0和1用于区分路径上是否存在前缀和为i的节点。那么当前子树对于ans的贡献就是:
f[0][0]∗g[0][0]+∑i∈[−d,d]f[i][0]∗g[−i][1]+f[i][1]∗g[−i][0]+f[i][1]∗g[−i][1]
公式中的d为当前子树的深度。
代码:
注意f和g数组的大小都是2N(存在正负),不然这道题有很多数组溢出了都发现不了……#include <cstdio> const int Maxn = 100010; typedef long long ll; inline ll Max(const ll &a, const ll &b) { return a > b ? a : b; } namespace IO { inline char get(void) { static char buf[1000000], *p1 = buf, *p2 = buf; if (p1 == p2) { p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin); if (p1 == p2) return EOF; } return *p1++; } inline void read(ll &x) { x = 0; static char c; for (; !(c >= '0' && c <= '9'); c = get()); for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); } inline void write(ll x) { if (!x) return (void)puts("0"); if (x < 0) putchar('-'), x = -x; static short s[12], t; while (x) s[++t] = x % 10, x /= 10; while (t) putchar('0' + s[t--]); putchar('\n'); } }; ll head[Maxn], sub; struct Edge { ll to, nxt, v; Edge(void) {} Edge(const ll &to, const ll &nxt, const ll &v) : to(to), nxt(nxt), v(v) {} } edge[Maxn << 1]; inline void add(ll a, ll b, ll v) { edge[++sub] = Edge(b, head[a], v), head[a] = sub; } ll S, root, siz[Maxn], son[Maxn], n, t[Maxn << 1], f[Maxn << 1][2], g[Maxn << 1][2], mdep, dep[Maxn], len[Maxn]; bool vis[Maxn]; inline void getroot(ll u, ll fa) { siz[u] = 1; son[u] = 0; for (ll i = head[u], v; i; i = edge[i].nxt) { v = edge[i].to; if (vis[v] || v == fa) continue; getroot(v, u); siz[u] += siz[v]; son[u] = Max(son[u], siz[v]); } son[u] = Max(son[u], S - siz[u]); if (son[u] < son[root]) root = u; } inline void dfs(ll u, ll fa) { mdep = Max(dep[u], mdep); f[len[u]][t[len[u]] > 0]++; t[len[u]]++; for (ll i = head[u], v; i; i = edge[i].nxt) { v = edge[i].to; if (vis[v] || v == fa) continue; dep[v] = dep[u] + 1; len[v] = len[u] + edge[i].v; dfs(v, u); } t[len[u]]--; } ll ans; inline void work(ll x) { vis[x] = 1; g [0] = 1; ll mx = 0; for (ll i = head[x], v; i; i = edge[i].nxt) { v = edge[i].to; if (vis[v]) continue; mdep = 1; dep[v] = 1; len[v] = edge[i].v + n; dfs(v, 0); mx = Max(mx, mdep); ans += (g [0] - 1) * f [0]; for (ll k = -mdep; k <= mdep; k++) ans += f[n + k][1] * g[n - k][1] + f[n + k][1] * g[n - k][0] + f[n + k][0] * g[n - k][1]; for (ll k = -mdep; k <= mdep; k++) { g[n + k][0] += f[n + k][0]; g[n + k][1] += f[n + k][1]; f[n + k][0] = f[n + k][1] = 0; } } for (ll i = -mx; i <= mx; i++) g[n + i][1] = g[n + i][0] = 0; for (ll i = head[x], v; i; i = edge[i].nxt) { v = edge[i].to; if (vis[v]) continue; S = siz[v], root = 0; getroot(v, 0); work(root); } } int main(void) { //freopen("in.txt", "r", stdin); //freopen("out.txt", "w", stdout); IO::read(n); for (ll i = 1, a, b, v; i < n; i++) { IO::read(a), IO::read(b), IO::read(v); if (!v) --v; add(a, b, v), add(b, a, v); } S = son[0] = n; getroot(1, 0); work(root); IO::write(ans); return 0; }
完。
By g1n0st
相关文章推荐
- 【点分治】BZOJ 3697:采药人的路径
- bzoj 3697: 采药人的路径 (点分治)
- 【BZOJ3697】采药人的路径【点分治】
- [BZOJ3697]采药人的路径(点分治)
- 【BZOJ3697】采药人的路径(点分治)
- bzoj 3697: 采药人的路径 点分治
- [BZOJ3697]采药人的路径(点分治)
- 【bzoj3697】采药人的路径 点分治
- BZOJ3697 采药人的路径 【点分治】
- 【BZOJ3697】采药人的路径 点分治
- BZOJ 3697: 采药人的路径 [点分治] [我想上化学课]
- BZOJ 3697 采药人的路径 树的点分治
- 【BZOJ 3697】采药人的路径 树上点分治
- 【bzoj 3697】采药人的路径(树的点分治)
- [bzoj3697]采药人的路径——点分治
- BZOJ 3697: 采药人的路径 树的点分治
- 【bzoj3697】【坑】采药人的路径 点分治
- [bzoj3697]采药人的路径_点分治
- BZOJ_3697_采药人的路径_点分治
- BZOJ 3697: 采药人的路径 点分治