[BZOJ3924][Zjoi2015]幻想乡战略游戏(动态点分治)

看到数据范围和6s的时限,得(cai)出是一道动态点分治。
这道题有一个巧妙的思路:
假设当前补给站为 u u ,并强制以u为根, v v u的一个子节点, sumdu s u m d u sumdv s u m d v 分别为 u u 的子树内的d之和以及 v v 的子树内的d之和, len(u,v) l e n ( u , v ) 为边 (u,v) ( u , v ) 的长度。
如果将补给站迁移到点 v v ,那么v的子树内的点到补给站的距离减少了 len(u,v) l e n ( u , v ) ,其他的点到补给站的距离增加了 len(u,v) l e n ( u , v ) 。也就是说,补给站迁移到点 v v 时,代价的增量为:

len(u,v)×(sumdusumdvsumdv)

整理一下,得出性质: u u 为根,v u u 的子节点,补给站在v u u 优,当且仅当:
2×sumdv>sumdu

显然满足条件的 v v 最多只有一个。
这时候,如果没有满足条件的v,则 u u 为最优位置。否则最优位置在v子树内
考虑动态点分治时每个点维护 d d sumd sumpd s u m p d 三个值,分别表示:
du d u :点 u u d值。
sumdu s u m d u :点 u u 的子树内所有的d值之和。
sumpdu s u m p d u :如果 u u 为根,则该值等于树中所有点v dis(v,u)×dv d i s ( v , u ) × d v 之和,否则等于 u u 的子树内所有点v dis(v,fau)×dv d i s ( v , f a u ) × d v 之和。
dis(a,b) d i s ( a , b ) a a b两点在原树中的距离, fau f a u 为点 u u 分治树上的父节点)
修改比较简单,从u开始不断地往 fau f a u 更新三个值即可。
询问时,先把最优解暂定为分治树的根节点。
假设现在到了点 u u 的分治子树,并且发现最优解在v的分治子树内( v v u在分治树上的一个子节点,原树上有边 (u,w) ( u , w ) 并且 w w v的分治子树内)
则可以转移到 v v 的分治子树内求解。
首先计算出u的分治子树内(除 v v 的分治子树内的点之外)的所有点到w dis×d d i s × d 之和(可以使用 sumpd s u m p d 计算得出),这样 v v 的分治子树之外节点的影响就计算好了。
此外,还要考虑节点w的影响因素:实际上对于 u u 的分治子树内但在v的分治子树之外的一个点 k k ,上面的计算只是计算好了w>k这一段路径的影响,而 v>w v − > w 这一段路径的影响还没就算出来。所以,还需要把 dw d w 加上 sumdusumdv s u m d u − s u m d v (对应的 sumd s u m d sumpd s u m p d 也要跟着调整),这样就处理好了 v>w v − > w 这一段路径的影响。这样就能进入 v v 的分治子树进行求解了。但v的分治子树求解完毕后,还需要把 dw d w 恢复原值。
最好要使用RMQ求LCA来查询两点距离。
复杂度 O(nlog2n) O ( n log 2 ⁡ n ) (设 n,q n , q 同阶)
第一次写动态点分治,调了一整个下午
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v; e; e = nxt[e])
#define Edge2(u) for (int e = adj2[u], v; e; e = nxt2[e])
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1e5 + 5, M = N << 1, LogN = 22;
int n, q, ecnt, nxt[M], adj[N], go[M], val[M], sze[N], maxs[N], G,
dep[N], dis[N], m, a[M], Log[M], RMQ[M][LogN], fir[N], fa[N],
Root, ecnt2, nxt2[N], adj2[N], go2[N], sc2[N];
ll d[N], sumd[N], sumpd[N];
bool vis[N];
void add_edge(int u, int v, int w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w;
}
void add_edge2(int u, int v, int w) {
    nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v; sc2[ecnt2] = w;
}
void dfs(int u, int fu) {
    dep[u] = dep[fu] + 1; a[fir[u] = ++m] = u;
    Edge(u) {
        if ((v = go[e]) == fu) continue;
        dis[v] = dis[u] + val[e]; dfs(v, u); a[++m] = u;
    }
}
void initRMQ()
{
    int i, j; For (i, 1, m) RMQ[i][0] = a[i];
    For (j, 1, 18) For (i, 1, m - (1 << j) + 1) {
        int x = RMQ[i][j - 1], y = RMQ[i + (1 << j - 1)][j - 1];
        RMQ[i][j] = dep[x] < dep[y] ? x : y;
    }
}
int dist(int u, int v) {
    int l = fir[u], r = fir[v]; if (l > r) swap(l, r);
    int x = Log[r - l + 1], r1 = RMQ[l][x], r2 = RMQ[r - (1 << x) + 1][x];
    int lca = dep[r1] < dep[r2] ? r1 : r2;
    return dis[u] + dis[v] - (dis[lca] << 1);
}
void dfs1(int u, int fu) {
    sze[u] = 1; for (int e = adj[u], v; e; e = nxt[e]) {
        if ((v = go[e]) == fu || vis[v]) continue;
        dfs1(v, u); sze[u] += sze[v];
    }
}
void dfs2(int r, int u, int fu) {
    maxs[u] = sze[r] - sze[u]; Edge(u) {
        if ((v = go[e]) == fu || vis[v]) continue;
        maxs[u] = max(maxs[u], sze[v]);
    }
    if (maxs[u] < maxs[G]) G = u;
    Edge(u) if ((v = go[e]) != fu && !vis[v]) dfs2(r, v, u);
}
void calcG(int u) {dfs1(u, 0); G = u; dfs2(u, u, 0);}
int dfs3(int u, int fu) {
    calcG(u); if (!Root) Root = G; vis[G] = 1; fa[G] = fu; int t = G;
    Edge(G) {
        if (vis[v = go[e]]) continue;
        int w = dfs3(v, t); add_edge2(t, w, v);
    }
    return t;
}
void change(int u, int delta) {
    int v = u; d[u] += delta; while (u) {
        sumd[u] += delta;
        sumpd[u] += 1ll * delta * dist(v, fa[u] ? fa[u] : u);
        u = fa[u];
    }
}
ll exc(int u, int w, int x) {
    ll ans = 0, cnt = d[u];
    Edge2(u) if ((v = go2[e]) != w) ans += sumpd[v], cnt += sumd[v];
    d[x] += cnt; return ans + cnt * dist(u, x);
}
ll query(ll ans, int u) {
    Edge2(u) if ((sumd[v = go2[e]] << 1) > sumd[u]) {
        ll wr = d[sc2[e]], tmp, delta = exc(u, v, sc2[e]);
        tmp = d[sc2[e]] - wr; for (int w = sc2[e]; w != u; w = fa[w])
            sumd[w] += tmp, sumpd[w] += tmp * dist(sc2[e], fa[w] ? fa[w] : w);
        ll nans = 0; for (int z = adj2[v]; z; z = nxt2[z]) nans += sumpd[go2[z]];
        ans = delta + query(nans, v); for (int w = sc2[e]; w != u; w = fa[w])
            sumd[w] -= tmp, sumpd[w] -= tmp * dist(sc2[e], fa[w] ? fa[w] : w);
        d[sc2[e]] = wr; return ans;
    }
    return ans;
}
int main() {
    int i, x, y, z; n = read(); q = read(); Log[0] = -1;
    For (i, 1, n << 1) Log[i] = Log[i >> 1] + 1;
    For (i, 1, n - 1)
        x = read(), y = read(), z = read(), add_edge(x, y, z);
    dfs(1, 0); initRMQ(); dfs3(1, 0); while (q--) {
        x = read(); y = read(); change(x, y);
        printf("%lld\n", query(sumpd[Root], Root));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值