P3920 [WC2014]紫荆花之恋

要求维护一颗带边权和点权 r r r 的树,支持动态插入叶子 以及 询问树上有多少点对满足两个点的点权之和大于两点间的边权之和,即 ∀ i < j , r i + r j ≥ dis ⁡ ( i , j ) \forall i < j,r_i+r_j \geq \operatorname{dis}(i,j) i<j,ri+rjdis(i,j)

强制在线。

1 ≤ 1\leq 1 点数 ≤ 1 0 5 \leq 10^5 105 1 ≤ 1 \leq 1 边权 ≤ 1 0 4 \leq 10^4 104,$1 \leq $ 点权 ≤ 1 0 9 \leq 10^9 109

std

先不考虑动态插入叶子,即给你一棵树,求出答案。

按照套路,对于这种大规模树上路径问题,考虑点分治。

考虑询问,由于点分治只统计经过根节点的方案数,那么设当前分治中心为 g g g,一条经过 g g g 的路径 ( u , v ) (u,v) (u,v) 可分为 ( u , g ) (u,g) (u,g) ( g , v ) (g,v) (g,v) 两段,那么 dis ⁡ ( u , v ) = dis ⁡ ( u , g ) + dis(g,v) ⁡ \operatorname{dis}(u,v)=\operatorname{dis}(u,g)+\operatorname{dis(g,v)} dis(u,v)=dis(u,g)+dis(g,v)

考虑到原式可移项为 r u − dis ⁡ ( u , g ) ≤ dis ⁡ ( g , v ) − r v r_u-\operatorname{dis}(u,g)\leq \operatorname{dis}(g,v)-r_v rudis(u,g)dis(g,v)rv,我们可以用一个数据结构,支持插入和求排名操作维护节点 g g g 的所有 dis ⁡ ( g , v ) − r v \operatorname{dis}(g,v)-r_v dis(g,v)rv,查询 r u − dis ⁡ ( u , g ) r_u-\operatorname{dis}(u,g) rudis(u,g) 的排名即可。

对于多次点分治,可用点分树优化。

再考虑动态插入叶子怎么做。

显然,定期重构部分点分树即可。

总时间复杂度为 O ( n n log ⁡ 2 n ) \mathcal O(n \sqrt n \log^2n) O(nn log2n)

#include <bits/stdc++.h>

using namespace std;

namespace Fread
{
    const int SIZE = 1 << 23;
    char buf[SIZE], *S, *T;
    inline char getchar()
    {
        if (S == T)
        {
            T = (S = buf) + fread(buf, 1, SIZE, stdin);
            if (S == T)
                return '\n';
        }
        return *S++;
    }
}

namespace Fwrite
{
    const int SIZE = 1 << 23;
    char buf[SIZE], *S = buf, *T = buf + SIZE;
    inline void flush()
    {
        fwrite(buf, 1, S - buf, stdout);
        S = buf;
    }
    inline void putchar(char c)
    {
        *S++ = c;
        if (S == T)
            flush();
    }
    struct NTR
    {
        ~NTR()
        {
            flush();
        }
    } ztr;
}

#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#define putchar Fwrite::putchar
#endif

#define ll long long

inline ll read()
{
    ll x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = x * 10 + c - '0', c = getchar();
    return x * f;
}

inline void write(ll x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

const int _ = 1e5 + 10, S = 350, inf = 1e7 + 10;

const double alpha = 0.95;

int n, r[_];

int sz[_];

bool vis[_];

int tot, head[_], to[_ << 1], nxt[_ << 1];

ll lst;

inline void add(int u, int v)
{
    to[++tot] = v;
    nxt[tot] = head[u];
    head[u] = tot;
}

inline pair<int, int> get_rt(int rt, int f, int pre)
{
    sz[rt] = 1;
    int mx = 0;
    pair<int, int> ans = {inf, 0};
    for (int i = head[rt]; i; i = nxt[i])
    {
        int v = to[i];
        if (vis[v] || v == f)
            continue;
        ans = min(ans, get_rt(v, rt, pre));
        sz[rt] += sz[v];
        mx = max(mx, sz[v]);
    }
    ans = min(ans, {max(mx, pre - sz[rt]), rt});
    return ans;
}

struct DIS
{
    int f[_][17], dep[_], dis[_];
    inline void insert(int x, int fa, int c)
    {
        f[x][0] = fa;
        dep[x] = dep[fa] + 1;
        dis[x] = dis[fa] + c;
        for (int i = 1; i <= 16; ++i)
            f[x][i] = f[f[x][i - 1]][i - 1];
    }
    inline int LCA(int x, int y)
    {
        if (dep[x] < dep[y])
            swap(x, y);
        for (int i = 16; ~i; --i)
            if (dep[f[x][i]] >= dep[y])
                x = f[x][i];
        if (x == y)
            return x;
        for (int i = 16; ~i; --i)
            if (f[x][i] != f[y][i])
                x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    inline int Dis(int x, int y)
    {
        return dis[x] + dis[y] - 2 * dis[LCA(x, y)];
    }
} bz;

struct ABC
{
    vector<int> a, b;
    inline void insert(int x)
    {
        b.insert(lower_bound(b.begin(), b.end(), x), x);
        if (b.size() >= S)
        {
            vector<int> c, d;
            int i = 0, j = 0;
            for (; i < a.size() && j < b.size();)
                if (a[i] < b[j])
                    c.push_back(a[i++]);
                else
                    c.push_back(b[j++]);
            while (i < a.size())
                c.push_back(a[i++]);
            while (j < b.size())
                c.push_back(b[j++]);
            swap(a, c);
            swap(b, d);
        }
    }
    inline int rank(int x)
    {
        return (upper_bound(a.begin(), a.end(), x) - a.begin()) + (upper_bound(b.begin(), b.end(), x) - b.begin()) - 2;
    }
    inline void clear()
    {
        a.clear();
        b.clear();
    }
} pa[_ << 1], pb[_ << 1];

struct D_F_Z
{
    int f[_], dep[_], siz[_];
    inline int query(int x, int r)
    {
        int ans = 0;
        for (int i = x, j; f[i]; i = j)
        {
            j = f[i];
            int dis = r - bz.Dis(x, j);
            ans += pa[j].rank(dis) - pb[i].rank(dis);
        }
        return ans;
    }
    inline void insert(int x, int v, int Rt)
    {
        pa[x].insert(-v);
        for (int i = x, j; i != Rt; i = j)
        {
            j = f[i];
            int dis = bz.Dis(x, j) - v;
            if (j != Rt)
                pa[j].insert(dis);
            pb[i].insert(dis);
        }
    }
    inline void clear(int x, int fa, int deps)
    {
        vis[x] = 0;
        for (int i = head[x]; i; i = nxt[i])
        {
            int v = to[i];
            if (v == fa || dep[v] <= deps)
                continue;
            clear(v, x, deps);
        }
    }
    inline int build(int x, int pre, int fa, int Rt)
    {
        int rt = get_rt(x, 0, pre).second, bas = sz[x];
        pa[rt].clear();
        pb[rt].clear();
        f[rt] = fa;
        siz[rt] = vis[rt] = 1;
        dep[rt] = dep[fa] + 1;
        insert(rt, r[rt], Rt);
        for (int i = head[rt]; i; i = nxt[i])
        {
            int v = to[i];
            if (vis[v])
                continue;
            siz[rt] += build(v, bas, rt, Rt);
        }
        return siz[rt];
    }
    inline void rebuild(int x)
    {
        if (!x)
            return;
        clear(x, 0, dep[x]);
        build(x, siz[x], f[x], f[x]);
    }
    inline void insert(int x, int v)
    {
        pa[x].insert(-v);
        siz[x]++;
        int Rt = 0;
        for (int i = x, j; f[i]; i = j)
        {
            j = f[i];
            int dis = bz.Dis(x, j) - v;
            siz[j]++;
            pa[j].insert(dis);
            pb[i].insert(dis);
            if (siz[i] > siz[j] * alpha)
                Rt = j;
        }
        rebuild(Rt);
    }
} t;

signed main()
{
    read(), n = read();
    for (int i = 1; i <= n; i++)
    {
        vis[i] = 1;
        int fa = read() ^ lst % 1000000000, c = read();
        r[i] = read();
        bz.insert(i, fa, c);
        if (i > 1)
        {
            add(fa, i);
            add(i, fa);
        }
        t.f[i] = fa;
        t.dep[i] = t.dep[fa] + 1;
        write(lst += t.query(i, r[i]));
        t.insert(i, r[i]);
        putchar('\n');
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值