【动态点分】绿老师 解题报告

来源:互联网 发布:魏晨退出非凡搭档 知乎 编辑:程序博客网 时间:2024/04/28 17:58

题目来源:2017北京八十中集训12.4考试题。出题人hhd

p.s. 这题有个dfs做法。但鉴于我从未写过动态点分,wxh佬建议我拿这道题练一下,作为我的第一道动态点分模板题。


Introduction

在线求树上最远点对

树有边权,给出k个黑点,求黑点间的最长距离。

如果是单次询问,当然可以dfs求直径O(n)。暂时不讲
或者静态点分单次O(nlogn)的naive做法
最好用动态点分,可以在线带修,O(klogn)

对于一个点,把它插入到点分树上所有祖先中(包括它自己)。对于另一个点,查询点它分树上所有祖先的信息来更新答案。再保证一下查询到不在同一子树的解(即每个点分中心维护来自不同子树的最大值和次大值),就可以保证每两个点都恰好在点分树的LCA处被查到一次
因此我们可以动态支持同时插入和查询

BZOJ1095 [ZJOI2007] 捉迷藏

参考链接:https://www.cnblogs.com/zzqsblog/p/6393023.html

一棵树,每个点有一个颜色(黑或白),一开始全是黑色。修改是将某个点的颜色反转,询问黑点间的最长距离。

简要题解

这是公认的动态点分模板题。
询问时,仍然是每个点分中心找到不在同一子树中的最大值和次大值来更新答案。
这道题就是上面那道简单题加上删除操作。因为最大值可能被删掉,可以用可删除的堆来维护最大值
在每个点开两个堆,第一个堆维护这个中心管辖的所有黑点到中心的距离,第二个堆维护所有分治儿子的堆顶。注意如果分治树上的重心是白点,那么不应该用单独一个来更新答案,如果只用一个黑点来更新答案是不合法的。为了统计答案,我们显然还要再开一个堆来维护答案
修改就只要沿着点分治树往根走,维护一下经过的祖先节点的堆即可。

Solution

绿老师把这个最远点对的问题扩展到二维。
基本思路仍然是每次点分统计跨过中心的答案

当然只能对a,b中的一维点分。
比如我们a点分,那么dis(ai,aj)就可以拆成d[ai]+d[aj](只要保证同一棵子树不会搞在一起)。这就相当于每个bi点带有d[ai]的点权,求带权最远点对。显然上面的做法处理端点点权简直无所畏惧。
需要注意的是点分的复杂度保证在于k=O(NlogN),但是由于相近的a点对应的b点却是完全分散的,因此每次都有n=N。不能用O(n)的dfs做法,否则总复杂度就变成O(N2)(跟暴力一样啊喂)。用动态点分的O(klogn)做,总复杂度就是O(NlogNlogN)
对于一个点分中心,把它的前i1个子树中a所对应的b插入,然后拿第i棵子树去查,查完再把第i棵子树插入。这样就可避免查到同一个子树中的a。点分中心本身对应的b,要么在所有子树之前一起插入,要么在所有子树之后一起查询即可。

综上,这是个静态点分套动态点分。先把点分树建出来,然后再开一个静态点分,内套动态点分求答案。

Debug

第一次写出了两个bug

bug #1

最开始时,我记fa[]为点分树上的父亲,dis[]为到fa的距离。
WA掉样例后发现一个点到其点分树祖先的距离不一定等于点分树上链dis求和,这个距离应该在原树上跑dfs来求。
因此要开dis[200000][18]来分别记录它到所有(logn个)祖先的距离。建点分树时,从每个点分中心出发,dfs它的管辖区域,来求得dis[][]数组。

bug #2

测了一次80分,发现WA了“ai都相同”的部分分。原来对于重合的a点没有处理好,究其原因是对于点分中心本身对应的b的处理方式没想清楚。
注意到点分中心对应的一堆b之间是可以互相查询的(这些a两两在点分树上的LCA确实是当前点分中心,需要在这里被统计)。因此应该查一个插一个,而不是一起操作(对,题解里是个flag,是我开始时的想法)。

Code

#include <cstdio>#include <vector>#define INF 9000000000000000LLtypedef long long ll;typedef std::vector<int> vec;inline void Max(ll &a, ll b) { if (a < b) a = b; }int n, m;vec b[100050];int h[100050], nx[200050], to[200050], e = 1;ll w[200050];inline void adde(int u, int v, ll d) {    to[e] = v; w[e] = d;    nx[e] = h[u]; h[u] = e++;}bool vis[100050];int sz[100050], mx[100050], hv[100050];int dfssize(int x, int f) {    sz[x] = b[x].size() + 1; mx[x] = 0;    for (int i = h[x]; i; i = nx[i])        if (!vis[to[i]] && to[i] != f) {            register int ret = dfssize(to[i], x);            sz[x] += ret;            if (ret > mx[x]) {                mx[x] = ret;                hv[x] = i;            }        }    return sz[x];}int root;int fa[100050][18], cnt[100050];ll dis[100050][18];vec son[100050];void setroot(int u, int f, ll d) {    fa[u][cnt[u]] = root;    dis[u][cnt[u]++] = d;    for (int i = h[u]; i; i = nx[i])        if (!vis[to[i]] && to[i] ^ f)            setroot(to[i], u, d + w[i]);}void divide(int u, int f) {    int Size = dfssize(u, f);    while (mx[u] > Size>>1) u = to[hv[u]];    setroot(root = u, u, 0);    vis[u] = true;    son[f].push_back(u);    for (int i = h[u]; i; i = nx[i])        if (!vis[to[i]]) divide(to[i], u);}ll ans = 0;ll d1[100050], d2[100050];int g[100050];void insert(int x, int f, ll d) {    for (int i = h[x]; i; i = nx[i])        if (vis[to[i]] && to[i] ^ f)            insert(to[i], x, d + w[i]);    for (int u : b[x])        for (int i = 0; i < cnt[u]; i++) {            register int p = fa[u][i], q = fa[u][i+1];            register ll v = d + dis[u][i];            if (v > d1[p]) {                if (q ^ g[p]) d2[p] = d1[p], g[p] = q;                d1[p] = v;            }            else if (v > d2[p] && q ^ g[p]) d2[p] = v;        }}void query(int x, int f, ll d) {    for (int i = h[x]; i; i = nx[i])        if (vis[to[i]] && to[i] ^ f)            query(to[i], x, d + w[i]);    for (int u : b[x])        for (int i = 0; i < cnt[u]; i++)            Max(ans, (fa[u][i+1] ^ g[fa[u][i]] ? d1 : d2)[fa[u][i]] + d + dis[u][i]);}void clear(int x, int f) {    for (int i = h[x]; i; i = nx[i])        if (vis[to[i]] && to[i] ^ f)            clear(to[i], x);    for (int u : b[x])        for (int i = cnt[u]-1, p; i >= 0 && ~g[p = fa[u][i]]; i--)            d1[p] = d2[p] = -INF, g[p] = -1;}void solve(int x) {    for (int i = h[x]; i; i = nx[i])        if (vis[to[i]]) {            query(to[i], x, w[i]);            insert(to[i], x, w[i]);        }    for (int u : b[x])        for (int i = 0; i < cnt[u]; i++) {            register int p = fa[u][i], q = fa[u][i+1];            register ll v = dis[u][i];            Max(ans, (q ^ g[p] ? d1 : d2)[p] + v);            if (v > d1[p]) {                if (q ^ g[p]) d2[p] = d1[p], g[p] = q;                d1[p] = v;            }            else if (v > d2[p] && q ^ g[p]) d2[p] = v;        }    clear(x,0);    vis[x] = false;    for (int nx : son[x]) solve(nx);}int main() {    freopen("forgive.in","r",stdin);    freopen("forgive.out","w",stdout);    scanf("%d%d",&n,&m);    int u, v; ll w;    for (int i = 1; i < n; i++) {        scanf("%d%d%lld",&u,&v,&w);        adde(u,v,w); adde(v,u,w);    }    while (m--) {        scanf("%d%d",&u,&v);        b[u].push_back(v);    }    divide(1,0);    clear(1,0);    solve(son[0][0]);    printf("%lld\n",ans);    return 0;}
原创粉丝点击