【Noip2016】天天爱跑步

题目描述

大意:给一颗n个点的有根树,每个点有一个期待的数值w[i]m次操作,每次往 st 的路径上插入一个首项为0公差为1的等差数列。结束后问每个点处有多少个值等于这个点期待的数值。
链接在这里

分析

我们来观察一下插入操作。考虑插入操作能够对哪些点产生影响,首先,需要在这个路径上,且期望值需要和等差数列对应项相同。前一个限制没有什么好说的,注意后一个限制。容易想到将这个路径分成两部分考虑,向上的(slca)和向下的(lcat)。
向上走的链是否有什么性质呢?每向上走一个,等差数列值+1,深度1,于是深度和等差数列值之和为定值,且等于起点的深度。于是对于一个向上走的链,当且仅当一个链上的点u满足以下等式,该链会对他产生贡献:dep[u]+w[u]=dep[s]。于是我们可以对点进行分类,dep[u]+w[u]相同的在一类中。于是每此操作我们只需在对应类别中找到在该链上的,并对其+1即可。处理一条链上的点,树链剖分是容易想到的。于是我们对每一个dep[u]+w[u]的值开一个线段树,由于空间限制,动态开点即可。每次在dep[u]+w[u]=dep[s]处找到对应链并+1
现在考虑向下走的链。方向变化之后,每次向下一层,等差数列值也加一。于是等差数列值与深度的差是定值,且等于lca处的值,即为(dep[s]dep[lca])dep[lca]=dep[s]2dep[lca]。所以当点u满足如下式子时,可以对其产生贡献:w[u]dep[u]=(dep[s]2dep[lca])。那么按照w[u]dep[u]也进行分类,开若干颗线段树,运用树剖在对应类里修改。为了避免出现负数,我们把所有w[u]dep[u]加上一个n即可。
现在考察一些细节。树剖修改的部分如何能正确区分这是向上走还是向下走呢?一种方式是分别从s跳到lca找寻上升那条链的线段树,从t跳到lca找寻下降那条链的线段树,然后最后删去一次lca(因为这样算了两遍)。另一种方法,也是我采取的方法是这样的。存下向上跳时要修改的线段树根节点编号,记为RT1,和向下走时要修改的线段树根节点编号,记为RT2。在树剖中swap(u,v)时,同时swap(RT1,RT2)。这在前面跳的过程中是显然正确的。现在考虑最后一段。如果当前我们是最后一次跳到的top比较高,那么这时应该去跳另一侧的,此时要swap(u,v),swap(RT1,RT2);否则,我们仍然应该跳这一侧的,不必交换。于是我们便解决了本题。

代码

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <set>
#define MAXN 300050
#define ri register int
using namespace std;
int n, m, ecnt, tcnt, idcnt, w[MAXN], ans[MAXN];
int fa[MAXN], dep[MAXN], top[MAXN], size[MAXN], son[MAXN], id[MAXN], rt[MAXN<<2], rt2[MAXN<<2];
struct node {
    int v; 
    node *next;
}pool[MAXN<<2], *h[MAXN];
struct Node {
    int ls, rs, lazy;
}t[MAXN<<5];
inline void adde(int u, int v) {
    node *p = &pool[ecnt++], *q = &pool[ecnt++];
    *p = node {v, h[u]}, h[u] = p;
    *q = node {u, h[v]}, h[v] = q;
}
void ins(int &u, int l, int r, int x) {
    if(!u) u = ++tcnt;
    if(l == r) return ;
    int mid = (l+r)>>1;
    if(x <= mid) ins(t[u].ls, l, mid, x);
    else ins(t[u].rs, mid+1, r, x);
}
void dfs1(int u) {
    size[u] = 1;
    for(node *p = h[u]; p; p = p->next) {
        if(p->v != fa[u]) {
            fa[p->v] = u, dep[p->v] = dep[u]+1;
            dfs1(p->v);
            size[u] += size[p->v];
            if(size[son[u]] <= size[p->v]) son[u] = p->v;
        }
    }
}
void dfs2(int u, int t) {
    top[u] = t;
    id[u] = ++idcnt;
    ins(rt[dep[u]+w[u]], 1, n, id[u]);
    ins(rt2[w[u]-dep[u]+n], 1, n, id[u]);
    if(!son[u]) return ;
    dfs2(son[u], t);
    for(node *p = h[u]; p; p = p->next)
        if(!id[p->v]) dfs2(p->v, p->v);
}
void change(int u, int l, int r, int tl, int tr, int w) {
    if(!u) return ;
    if(tl <= l && r <= tr) return (void)(t[u].lazy += w);
    int mid = (l+r)>>1;
    if(tl <= mid) change(t[u].ls, l, mid, tl, tr, w);
    if(mid < tr) change(t[u].rs, mid+1, r, tl, tr, w);
}
int Get(int u, int v) {
    while(top[u] != top[v]) {
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        u = fa[top[u]];
    }
    if(id[u] > id[v]) swap(u, v);
    return u;
}
void Change(int u, int v) {
    int lca = Get(u, v);
    int RT1 = rt[dep[u]], RT2 = rt2[dep[u]-2*dep[lca]+n];
    while(top[u] != top[v]) {
        if(dep[top[u]] < dep[top[v]]) swap(u, v), swap(RT1, RT2);
        change(RT1, 1, n, id[top[u]], id[u], 1);
        u = fa[top[u]];
    }
    if(id[v] > id[u]) swap(u, v), swap(RT1, RT2);
    change(RT1, 1, n, id[v], id[u], 1);
}
int query(int u, int l, int r, int x) {
    if(l == r) return t[u].lazy;
    int mid = (l+r)>>1;
    if(t[u].lazy) 
        t[t[u].ls].lazy += t[u].lazy, t[t[u].rs].lazy += t[u].lazy, t[u].lazy = 0;
    if(x <= mid) return query(t[u].ls, l, mid, x);
    return query(t[u].rs, mid+1, r, x);
}
int main () {
    int u, v;
    scanf("%d%d", &n, &m);
    for(ri i = 1; i < n; ++i) scanf("%d%d", &u, &v), adde(u, v);
    for(ri i = 1; i <= n; ++i) scanf("%d", &w[i]);
    dfs1(1), dfs2(1, 1);
    while(m--) {
        scanf("%d%d", &u, &v);
        Change(u, v);
    }
    for(ri i = 1; i <= n; ++i) {
        ans[i] = query(rt[dep[i]+w[i]], 1, n, id[i]);
        ans[i] += query(rt2[w[i]-dep[i]+n], 1, n, id[i]);
        printf("%d ", ans[i]);
    }
    return 0;
}
posted @ 2018-08-28 19:37  hychyc1  阅读(116)  评论(0编辑  收藏  举报