【BZOJ 3924】【ZJOI 2015】幻想乡战略游戏
http://www.lydsy.com/JudgeOnline/problem.php?id=3924
gty的测试题,不会动态点分治而且看不出来链剖做法而且暴力打残所以这道题喜闻乐见的爆零了qwq
动态点分治:设重心重构树上以x为根的子树为\(T_x\),在重心重构树上每个点维护3个值。
\(sum(x)=\sum\limits_{u\in T_x}d(u)\)
\(ans(x)=\sum\limits_{u\in T_x}d(u)*dis(u,x)\)
\(ans\_fa(x)=\sum\limits_{u\in T_x}d(u)*dis(u,fa(x))\)
其中,\(dis\)指的是原树上的两点间距离,这个可以用st表\(O(1)\)求出;\(fa(x)\)指的是重心重构树上x的父亲。
每次修改x时,在重心重构树上不断地往上跳同时维护这三个值就可以了。
要查询一个点的花费(这里的花费指的在整棵树上的花费),类似修改,从重心重构树上不断地往上跳并统计答案(\(ans\_fa\)很明显是用来减掉自身贡献的)。
查询时从重心重构树的树根开始,枚举当前点在原树上连向更小分治块的边,检查这些边直接连着的点的花费。要是没有比当前点小的,答案就是当前点的花费;否则当前点跳到此时花费最小的点所在的下一级分治块的重心,继续检查。
感性理解:因为任意一条链上的点的花费是单峰的(峰向下),每次都向峰靠拢,所以这么做是正确的。
又因为重心重构树的树高是\(O(\log n)\)的,查询一个点的花费的时间复杂度是\(O(\log n)\),所以总时间复杂度是\(O(n\log n+q\log^2n*d)\)。
d指的是检查的所有点的平均度数,因为cls原题面中说明了每个点的度不超过20,所以这样可过。
这道题适合做动态点分治的模板题啊qwq
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 100003;
namespace RE {
struct node {int nxt, to, w;} E[N];
int cnt = 0, point[N], fa[N];
ll sum[N], ans[N], ans_fa[N];
void ins(int u, int v) {E[++cnt] = (node) {point[u], v}; point[u] = cnt; fa[v] = u;}
}
namespace ORG {
struct node {int nxt, to, w;} E[N << 1];
int cnt = 0, point[N];
bool vis[N];
vector <pair <int, int> > ref[N];
void ins(int u, int v, int e) {E[++cnt] = (node) {point[u], v, e}; point[u] = cnt;}
int qu[N], fa[N], sz[N];
int findrt(int x) {
fa[qu[1] = x] = 0;
int p = 0, q = 1, u;
while (p != q) {
sz[u = qu[++p]] = 0;
for (int i = point[u]; i; i = E[i].nxt)
if (E[i].to != fa[u] && !vis[E[i].to])
fa[E[i].to] = u, qu[++q] = E[i].to;
}
for (int i = q; i >= 1; --i) {
sz[fa[qu[i]]] += (++sz[qu[i]]);
if ((sz[qu[i]] << 1) > q)
return qu[i];
}
}
int root;
void dfs(int x) {
vis[x] = true;
for (int i = point[x]; i; i = E[i].nxt)
if (!vis[E[i].to]) {
root = findrt(E[i].to);
ref[x].push_back(make_pair(E[i].to, root));
RE::ins(x, root);
dfs(root);
}
}
int L[N], wat[N << 1], tt = 0;
ll f[N << 1][19], deep[N];
void dfs2(int x, int ff) {
wat[L[x] = ++tt] = x;
for (int i = point[x]; i; i = E[i].nxt)
if (E[i].to != ff) {
deep[E[i].to] = deep[x] + E[i].w;
dfs2(E[i].to, x);
wat[++tt] = x;
}
}
int Log_2[N << 1], rt;
void pre() {
dfs2(1, 0);
Log_2[1] = 0; int cc = 0;
for (int i = 2; i <= tt; ++i) {
Log_2[i] = cc;
if ((1 << (cc + 1)) == i)
++cc;
}
for (int i = 1; i <= tt; ++i)
f[i][0] = deep[wat[i]];
for (int j = 1; j <= 18; ++j)
for (int i = (1 << j); i <= tt; ++i)
f[i][j] = min(f[i][j - 1], f[i - (1 << (j - 1))][j - 1]);
rt = 1; while (RE::fa[rt]) rt = RE::fa[rt];
}
int len;
ll dis(int x, int y) {
ll r = deep[x] + deep[y];
x = L[x]; y = L[y];
if (x > y) x ^= y ^= x ^= y;
len = Log_2[y - x + 1];
ll dlca = min(f[y][len], f[x - 1 + (1 << len)][len]);
return r - (dlca << 1);
}
ll query(int x) {
ll ret = RE::ans[x], retf;
int ff, tmp = x;
while (true) {
if ((ff = RE::fa[tmp]) == 0) return ret;
retf = RE::ans[ff] - RE::ans_fa[tmp];
ret += retf + (RE::sum[ff] - RE::sum[tmp]) * dis(x, ff);
tmp = ff;
}
}
void change(int x, int e) {
int tmp = x;
while (true) {
RE::sum[tmp] += e;
RE::ans[tmp] += dis(x, tmp) * e;
if (RE::fa[tmp]) {
RE::ans_fa[tmp] += dis(x, RE::fa[tmp]) * e;
tmp = RE::fa[tmp];
} else
return;
}
}
ll ansit() {
int tmp = rt, an = tmp, len;
pair <int, int> ann;
ll annow, cmpan, rr;
while (true) {
cmpan = annow = query(tmp);
for (int i = 0, len = ref[tmp].size(); i < len; ++i)
if ((rr = query(ref[tmp][i].first)) < annow)
annow = rr, ann = ref[tmp][i];
if (annow != cmpan)
tmp = ann.second;
else
return annow;
}
}
}
int n;
int main() {
int q;
scanf("%d%d", &n, &q);
int u, v, e;
for (int i = 1; i < n; ++i) {
scanf("%d%d%d", &u, &v, &e);
ORG::ins(u, v, e);
ORG::ins(v, u, e);
}
ORG::dfs(ORG::findrt(1));
ORG::pre();
while (q--) {
scanf("%d%d", &u, &e);
ORG::change(u, e);
printf("%lld\n", ORG::ansit());
}
return 0;
}