洛谷 P3920 [WC2014]紫荆花之恋

强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。

仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点,每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 \(i\) 都有一个感受能力值 \(r_i\),小精灵 \(i,j\) 成为朋友当且仅当在树上 \(i\) 和 \(j\) 的距离 \(dist(i,j) \leq r_i+r_j\),其中 \(dist(i,j)\) 表示在这个树上从 \(i\) 到 \(j\) 的唯一路径上所有边的边权和。

强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。

我们假定这个树一开始为空,节点按照加入的顺序从 1 开始编号。由于强强非常好奇,你必须在他每次出现新结点后马上给出总共的朋友对数,不能拖延哦。

\(1 \leq c_i \leq 10^4\),\(a_i \leq 2\times 10^9\),\(r_i \leq 10^9\) ,强制在线。


这题就他妈离谱好吧,给我做傻了。

先考虑不加点怎么算答案,我们对树点分治,设点 \(u\) 到分治中心的距离是 \(dis_u\) ,那么我们要计算满足 \(dis_u+dis_v\le r_u+r_v\) 的个数,那么就有 \(dis_u - r_u\le r_v - dis_v\) ,这明显是个偏序,直接双指针扫就行,发现子树多算了,再容斥掉子树自己的。

然而这个东西完全不能支持加点,我们每次加完点需要算出所有和当前点 \(u\) 满足条件的点对,然而你总不能双指针扫一遍吧,所以我们需要数据结构来维护,因为边权和点权都很大,没法离散化,所以只能用平衡树来维护,我们建出当前树的点分树,对于一个点维护他的祖先、到这个祖先的距离、他所在祖先儿子的子树的平衡树(只算子树的),然后每个点建出平衡树来维护子树每个点的 \(dis_u-r_u\) ,于是我们每次加完点之后跳当前的祖先就可以算出答案。

但是这玩意是静态的呀,而我们每次点分治肯定是不行的,所以我们硬着头皮每次只重构一部分点分树。

具体来说,我们应用替罪羊树的思想,加入点后我们看看树上是否有一个点的子树大小乘阈值小于某个儿子的子树大小,也就是 \(size_u\times \alpha < size_{son_u}\) ,如果有,那么就重构当前点的子树的联通块,最好是从上往下,找到一个最浅的点,这样子我们就可以保证点分树形态是个比较优美的了。

比较需要注意的一点就是一定要写比较快的平衡树, \(fhq,splay\) 肯定是不行的,所以我写的替罪羊树。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
const int N = 2e5;
const int M = 4e6;
const int inf = 1e9;
const double alpha = 0.75;
using namespace std;
struct edges
{
    int to,cost;
}edge[N * 2 + 5];
int n,pool[M + 5],tot,cnt,q[M + 5],r[N + 5],rt[M + 5],nxt[N * 2 + 5],head[N + 5],edge_cnt,rtc,sz[N + 5];
long long ans;
struct info
{
    int p,len,rt;
}tmp;
vector <info> d[N + 5];
vector <info>::iterator it;
vector <int> son[N + 5];
void add_edge(int u,int v,int w)
{
    edge[++edge_cnt] = (edges){v,w};
    nxt[edge_cnt] = head[u];
    head[u] = edge_cnt;
}
void init(int n)
{
    for (int i = n;i >= 1;i--)
        pool[++tot] = i;
}
struct node
{
    int ch[2],size,sz1,sz2,cnt,val;
};
struct Goat
{
    node t[M + 5];
    int node_cnt;
    #define lc(k) t[k].ch[0]
    #define rc(k) t[k].ch[1]
    int new_node(int x)
    {
        int k = pool[tot--];
        lc(k) = rc(k) = 0;
        t[k].size = t[k].sz1 = t[k].sz2 = t[k].cnt = 1;
        t[k].val = x;
        return k;
    }
    void pushup(int k)
    {
        t[k].size = t[lc(k)].size + t[rc(k)].size + t[k].cnt;
        t[k].sz1 = t[lc(k)].sz1 + t[rc(k)].sz1 + (t[k].cnt != 0);
        t[k].sz2 = t[lc(k)].sz2 + t[rc(k)].sz2 + 1;
    }
    bool balance(int k)
    {
        return t[k].size && (t[k].sz2 * alpha <= max(t[lc(k)].sz2,t[rc(k)].sz2) || t[k].sz2 * alpha >= t[k].sz1);
    }
    void dfs(int k)
    {
        if (!k)
            return;
        dfs(lc(k));
        if (t[k].cnt)
            q[++cnt] = k;
        else
            pool[++tot] = k;
        dfs(rc(k));
    }
    void clears(int k)
    {
        if (!k)
            return;
        clears(lc(k));
        pool[++tot] = k;
        clears(rc(k));
    }
    void clear(int &k)
    {
        clears(k);
        k = 0;
    }
    int build(int l,int r)
    {
        if (l > r)
            return 0;
        if (l == r)
        {
            lc(q[l]) = rc(q[l]) = 0;
            t[q[l]].size = t[q[l]].cnt;
            t[q[l]].sz1 = t[q[l]].sz2 = 1;
            return q[l];
        }
        int mid = l + r >> 1;
        lc(q[mid]) = build(l,mid - 1);
        rc(q[mid]) = build(mid + 1,r);
        pushup(q[mid]);
        return q[mid];
    }
    void rebuild(int &k)
    {
        if (!k)
            return;
        cnt = 0;
        dfs(k);
        k = build(1,cnt);
    }
    void insert(int &k,int x)
    {
        if (!k)
            return (void)(k = new_node(x));
        if (t[k].val == x)
            t[k].cnt++;
        else
            if (x < t[k].val)
                insert(lc(k),x);
            else
                insert(rc(k),x);
        pushup(k);
        if (balance(k))
            rebuild(k);
    }
    int query(int k,int x)
    {
        if (!k)
            return 0;
        if (x == t[k].val)
            return t[lc(k)].size + t[k].cnt;
        if (x < t[k].val)
            return query(lc(k),x);
        return t[lc(k)].size + t[k].cnt + query(rc(k),x);
    }
    void print(int k)
    {
        if (!k)
            return;
        print(lc(k));
        cout<<k<<" "<<t[k].val<<" "<<t[k].cnt<<endl;
        print(rc(k));
    }
}tree;
int vis[N + 5],size[N + 5],maxp[N + 5],Rt,sm,fa[N + 5],dis[N + 5];
void dfs(int u,int fa)
{
    size[u] = 1;maxp[u] = 0;
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i].to;
        if (!vis[v] || v == fa)
            continue;
        dfs(v,u);
        size[u] += size[v];
        maxp[u] = max(maxp[u],size[v]);
    }
    maxp[u] = max(maxp[u],sm - size[u]);
    if (maxp[u] < maxp[Rt])
        Rt = u;
}
void getdis(int u,int fa,int p)
{
    son[p].push_back(u);
    tree.insert(rt[p],dis[u] - r[u]);
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i].to,w = edge[i].cost;
        if (!vis[v] || v == fa)
            continue;
        dis[v] = dis[u] + w;
        getdis(v,u,p);
    }
}
void get(int u,int fa)
{
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i].to;
        if (!vis[v] || v == fa)
            continue;
        tmp.len = dis[v];
        tree.insert(rt[tmp.rt],dis[v] - r[v]);
        d[v].push_back(tmp);
        get(v,u);
    }
}
void calc(int u)
{
    son[u].clear();
    dis[u] = 0;
    getdis(u,0,u);
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i].to;
        if (!vis[v])
            continue;
        tmp.p = u;
        tmp.len = dis[v];
        tmp.rt = ++rtc;
        tree.insert(rt[tmp.rt],dis[v] - r[v]);
        d[v].push_back(tmp);
        get(v,u);
    }
}
void solve(int u,int f)
{
    vis[u] = 0;
    sz[u] = 1;
    calc(u);
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i].to;
        if (!vis[v])
            continue;
        Rt = 0;
        maxp[Rt] = sm = size[v];
        dfs(v,u);
        solve(Rt,u);
        sz[u] += sz[Rt];
    }
}
void rebuild(int x)
{
    for (int i = 0;i < son[x].size();i++)
    {
        int v = son[x][i];
        vis[v] = 1;
        sz[v] = 0;
        while (!d[v].empty() && d[v].back().p != x && v != x)
        {
            tree.clear(rt[d[v].back().rt]);
            d[v].pop_back();
        }
        if (!d[v].empty() && d[v].back().p == x)
        {
            tree.clear(rt[d[v].back().rt]);
            d[v].pop_back();
        }
        tree.clear(rt[v]);
    }
    Rt = 0;
    maxp[Rt] = sm = son[x].size();
    dfs(x,0);
    solve(Rt,0);
}
int main()
{
	//freopen("16.in","r",stdin);
	//freopen("a1.out","w",stdout);
    scanf("%d",&n);
    scanf("%d",&n);
    init(M);
    int a,c;
    scanf("%d%d%d",&a,&c,&r[1]);
    dis[1] = 0;
    cout<<0<<endl;
    tree.insert(rt[1],-r[1]);
    son[1].push_back(1);
    rtc = n;
    sz[1] = 1;
    for (int i = 2;i <= n;i++)
    {
        scanf("%d%d%d",&a,&c,&r[i]);
        a ^= ans % inf;
        add_edge(a,i,c);
        add_edge(i,a,c);
        son[i].push_back(i);
        sz[i] = 1;
        son[a].push_back(i);
        sz[a]++;
        for (it = d[a].begin();it != d[a].end();it++)
        {
            tmp = *it;
            tmp.len += c;
            sz[tmp.p]++;
            son[tmp.p].push_back(i);
            tree.insert(rt[tmp.rt],tmp.len - r[i]);
            d[i].push_back(tmp);
        }
        tmp.p = a;tmp.len = c;tmp.rt = ++rtc;
        tree.insert(rt[rtc],tmp.len - r[i]);
        d[i].push_back(tmp);
        tree.insert(rt[i],-r[i]);
        for (it = d[i].begin();it != d[i].end();it++)
        {
            int p = it -> p,len = it -> len;
            tree.insert(rt[p],len - r[i]);
        }
        for (it = d[i].begin();it != d[i].end();it++)
        {
            int p = it -> p,len = it -> len;
            ans += tree.query(rt[p],r[i] - len) - tree.query(rt[it -> rt],r[i] - len);
        }
        for (it = d[a].begin();it != d[a].end();it++)
        {
            int p = it -> p;
            if (p == d[a].back().p)
                continue;
            if (son[p].size() * alpha < son[(*(it + 1)).p].size())
            {
                rebuild(p);
                break;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
上一篇:【46】(快慢指针)删除排序数组中的重复项(LC 26)


下一篇:JMeter配置文件jmeter.properties配置项说明