2020 牛客多校7 C A National Pandemic(树链剖分)

*爱你&永不变心* 提交于 2020-08-04 10:10:31

题意

\(T\)组样例,第二行给\(n\),\(m\),分别为点的个数和询问个数,接下来\(n-1\)行为边,之后是\(m\)行询问
询问有三种,\(1\) \(x\) \(w\), 表示点\(x\)增加\(w\),其他所有点增减\(w-dis(x,y)\)(点\(y\)树上到\(x\)的距离)
\(2\) \(x\),表示将点\(x\)的权值变为\(min(F(x), 0)\)
\(3\) \(x\),表示询问\(x\)的权值\(F(x)\)


解法

对整颗树进行树链剖分(对各点做标记的部分只有两个dfs而已,不是很难),之后将重新标号的点映射到线段树上,并用线段树维护每个点的权值(除了2操作可以使用另外一个数组来维护每个点被减去的权值),操作1我们只需要维护一个全局变量,然后对x到根上所有的点的点权全部+2,这样我们就可以假装所有的1操作都是在根上操作的了( ̄▽ ̄)/。最后计算结果时的式子为\(addw[x] - 1ll * cnt1 * dep[x] + golbal_wt + queryPath(1, x)\) 就是\(2\)操作的修改值 \(-\) 之前\(1\)操作的次数 \(*\) \(x\)的深度 \(+\) 全局权值 \(+\) 树上\(1\)\(x\)的点权

先拿例题中的图举例
首先将树剖成三条链

经过操作\(1\) \(1\) \(5\)之后,全局变量变为\(4\),\(1\)的点权变为\(2\),又经过\(2\) \(1\), \(1\)的点权修正值为\(-5\)

再经过\(1\) \(2\) \(7\)之后,\(1\), \(2\)的点权继续加\(2\),全局变量\(+ 5 = 9\)






这样操作的时候,比如你要求点\(3\)的值,就是\(全局权重-dep[3] * 2 + queryPath(1,3) = 9\), 因为有两次操作\(1\),而权值公式为\(w-dis(x,y)\),所以减去两次\(dep[3]\),最后我们补上多减的值就是了。

又比如我们询问\(2\), 权值如上计算是\(11\),按照最基本的式子原本应该是\(全局权重-cnt1*dep[2]\), 而因为\(1\),\(2\)都曾进行过一次操作,我们应该将操作点移到根后点\(2\)失去的贡献加回来,也就是每经过一个点,我们就将权重\(+2\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 5e4 + 7;

int t;
int n, m, u, v, op, w;
int head[N], cnte = 0, idx = 0;
int dep[N], dfn[N], fa[N], top[N], wson[N], siz[N], id[N];
ll addw[N], global_wt;


struct Edge{
    int to, nxt, val;
} edge[N << 1];
void addedge(int u, int v)
{
    edge[++cnte].nxt = head[u];
    edge[cnte].to = v;
    edge[cnte].val = 0;
    head[u] = cnte;
}

struct segment_tree
{
    #define lson rt << 1
    #define rson rt << 1 | 1

    ll sum[N << 2], add[N << 2];

    void udp(int rt, ll v, int l, int r)
    {
        sum[rt] += (r - l + 1ll) * v;
        add[rt] += v;
    }
    void build(int l, int r, int rt)
    {
        sum[rt] = add[rt] = 0;

        if (l == r) return;
        int mid = (l + r) >> 1;
        build(l, mid, lson);
        build(mid + 1, r, rson);
    }
    void push_down(int rt, int l, int r)
    {
        if(add[rt])
        {
            int mid = (l + r) >> 1;
            udp(lson, add[rt], l, mid);
            udp(rson, add[rt], mid + 1, r);
            add[rt] = 0;
        }
    }
    void update(int L, int R, ll c, int l, int r, int rt)
    {
        if (L == l && r == R )
        {
            udp(rt, c, l, r);
            return;
        }
        int mid = (l + r) >> 1;
        push_down(rt, l, r);

        if (L <= mid) update(L, min(R,mid), c, l, mid, lson);
        if (R > mid) update(max(L,mid + 1), R, c, mid + 1, r, rson);
        sum[rt] = sum[lson] + sum[rson];
    }
    ll query(int L, int R, int l, int r, int rt)
    {
        if (L == l && r == R) return sum[rt];
        int mid = (l + r) >> 1;
        push_down(rt, l, r);
        ll res = 0;
        if (R <= mid) res += query(L, R, l, mid, lson);
        else if (L > mid) res += query(L, R, mid + 1, r, rson);
        else return query(L, mid, l, mid, lson) + query(mid + 1, R, mid + 1, r, rson);
    }
} tree;

void init()
{
    cnte = 0;
    idx = 0;
    global_wt = 0;
    fill(head, head + N, 0);
    fill(fa, fa + N, 0);
    fill(siz, siz + N, 0);
    fill(wson, wson + N, 0);
    fill(addw, addw + N, 0);

}

void dfs1(int u)
{
    siz[u] = 1;
    for (int i = head[u]; i;i = edge[i].nxt)
    {
        int v = edge[i].to;
        if(v == fa[u]) continue;
        fa[v] = u;
        dep[v] = dep[u] + 1;
        dfs1(v);
        siz[u] += siz[v];
        if(siz[v] > siz[wson[u]]) wson[u] = v;
    }
}

void dfs2(int u, int chain)
{
    id[u] = ++idx, dfn[idx] = u;
    top[u] = chain;

    if(wson[u] != 0) dfs2(wson[u], chain);
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        if(v == fa[u] || v == wson[u]) continue;
        dfs2(v, v);
    }
}

void udpPath(int u, int v, int val)
{
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        tree.update(id[top[u]], id[u], val, 1, n, 1);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    tree.update(id[v], id[u], val, 1, n, 1);
}


ll queryPath(int u, int v)
{
    ll res = 0;
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        res += tree.query(id[top[u]], id[u], 1, n, 1);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    res += tree.query(id[v], id[u], 1, n, 1);
    return res;
}



void solve()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i < n; i++)
    {
        scanf("%d %d", &u, &v);
        addedge(u, v);
        addedge(v, u);
    }
    tree.build(1, n, 1);
    dep[1] = 1;
    dfs1(1);
    dfs2(1, 1);
    int cnt1 = 0;
    while(m--)
    {
        scanf("%d", &op);
        if(op == 1)
        {
            scanf("%d %d", &u, &w);
            global_wt += w;
            global_wt -= dep[u];
            cnt1++;
            udpPath(1, u, 2);
        }
        else if(op == 2)
        {
            scanf("%d", &u);
            ll weight = addw[u] - 1ll * cnt1 * dep[u] + global_wt + queryPath(1, u);
            if(weight > 0) addw[u] -= weight;
        }
        else
        {
            scanf("%d", &u);
            ll weight = addw[u] - 1ll * cnt1 * dep[u] + global_wt + queryPath(1, u);
            printf("%lld\n", weight);
        }
        
    }
}

int main()
{
    scanf("%d", &t);
    while(t--)
    {       
        init();
        solve();
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!