• A National Pandemic (思维 + 树链剖分模版)


    题目链接:https://ac.nowcoder.com/acm/contest/5672/C

    题目大意:

    一棵无根树,每个点都有一个点权 f(x),其初值均为 0,有三种操作。
    操作1:对所有的 y,修改f(y) 为 w - dist(x,y)
    操作2:修改 f(x) 为 min(f(x),0)
    操作3:查询 f(x)

    想法:

    对于操作一: w - dist(x,y) = w - dep[x] - dep[y] + 2 * dep[lca]   对于 dep[x] 和 dep[y] 我们可以直接得到,所以现在问题转化为如何维护 2 * dep[lca]

    所以对于操作一之后对于当前的 x ,我们的答案就是 ans = sum【代表所有的w的和】 + qchain(x,1)【1->x的修改次数,即cnt】 -  dep[x] * op1 【op1 代表进行了几次操作一】- dist 【所有的dep[y]的和】- op2[x] 【操作二取最小值导致的差值】

    因为操作二的要么是f[x] 要么是 0,我们不妨就维护一个最大值,每次变化的最终结果就是之前的值减去的最大的那个

    其余的基本上就是树链剖分的板子了

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    
    #define ll long long
    #define ull unsigned long long
    #define ls nod<<1
    #define rs (nod<<1)+1
    #define pii pair<int,int>
    #define mp make_pair
    #define pb push_back
    #define INF 0x3f3f3f3f
    #define max(a, b) (a>b?a:b)
    #define min(a, b) (a<b?a:b)
    
    
    const double eps = 1e-8;
    const int maxn = 2e5 + 10;
    const ll MOD = 1e9 + 7;
    const int mlog=20;
    
    int sgn(double a) { return a < -eps ? -1 : a < eps ? 0 : 1; }
    
    using namespace std;
    
    int n,m;
    int head[maxn],cnt;
    struct Graph {
        int to,nxt;
    }edge[maxn];
    
    void add_edge(int u,int v) {
        edge[cnt].to = v;
        edge[cnt].nxt = head[u];
        head[u] = cnt++;
    
        edge[cnt].to = u;
        edge[cnt].nxt = head[v];
        head[v] = cnt++;
    }
    
    int v[maxn]; // 结点权值
    int fa[maxn]; // 父亲
    int dep[maxn]; // 深度
    int siz[maxn]; // 大小
    int son[maxn]; // 重儿子
    
    // 第一遍的dfs可以得到深度,父亲,大小,重儿子
    void dfs1(int u,int f){ // u是当前结点,f是u的父亲
        fa[u] = f;
        dep[u] = dep[f] + 1;
        siz[u] = 1;
        int maxsize = -1;  // 判断是不是重儿子的一个临时变量
        for (int i=head[u];~i;i=edge[i].nxt){
            int v = edge[i].to;
            if (v == f)   //如果是父亲肯定不可以
                continue;
            dfs1(v,u);
            siz[u] += siz[v];
            if (siz[v] > maxsize){
                maxsize = siz[v];
                son[u] = v;  // u的重儿子就是v
            }
        }
    }
    
    int tim; // 时间戳计数器
    int dfn[maxn]; // 时间戳
    int top[maxn]; //重链的顶部
    int w[maxn]; // 结点权值dfs序
    
    void dfs2(int u,int t){ // u是当前结点,t是当前结点的重链的头
        dfn[u] = ++tim;
        top[u] = t;
        w[tim] = v[u];
        if (!son[u])  // 如果不存在重儿子,那么它就是叶子结点,退出
            return;
        dfs2(son[u],t);
        for (int i=head[u];~i;i=edge[i].nxt){
            int v = edge[i].to;
            if (v == fa[u] || v == son[u])   // 往上遍历肯定不可以了,因为前面的dfs2已经遍历了重儿子所以这里也没必要了
                continue;
            dfs2(v,v);   // 此时这个肯定是轻儿子
        }
    }
    
    struct segment_tree{
        int l,r;
        ll val,lazy;
    }tree[maxn*4];
    
    void pushup(int nod){
        tree[nod].val = (tree[nod<<1].val + tree[(nod<<1)+1].val);
    }
    
    void pushdown(int nod){
        tree[nod<<1].lazy += tree[nod].lazy;
        tree[(nod<<1)+1].lazy += tree[nod].lazy;
        tree[nod<<1].val += (tree[nod<<1].r-tree[nod<<1].l + 1) * tree[nod].lazy ;
        tree[(nod<<1)+1].val += (tree[(nod<<1)+1].r-tree[(nod<<1)+1].l+1) * tree[nod].lazy ;
        tree[nod].lazy = 0;
    }
    
    void build(int l,int r,int nod=1){
        tree[nod].l = l;
        tree[nod].r = r;
        tree[nod].lazy = 0;
        tree[nod].val = 0;
        if (l == r){
            tree[nod].lazy = 0;
            tree[nod].val = 0;
            return ;
        }
        int mid = (l+r)>>1;
        build(l,mid,nod<<1);
        build(mid+1,r,(nod<<1)+1);
        pushup(nod);
    }
    
    void modify(int x,int y,int z,int k=1){
        int l = tree[k].l, r = tree[k].r;
        if (x<= l && y>=r){
            tree[k].lazy += z;
            tree[k].val += (r-l+1) * z;
            return ;
        }
        if (tree[k].lazy)
            pushdown(k);
        int mid = (l+r)>>1;
        if (x<=mid){
            modify(x,y,z,k<<1);
        }
        if (y>mid){
            modify(x,y,z,(k<<1)+1);
        }
        pushup(k);
    }
    
    ll query(int x,int y,int k=1){
        int l = tree[k].l,r = tree[k].r;
        if (x<=l && y>=r){
            return tree[k].val;
        }
        if (tree[k].lazy){
            pushdown(k);
        }
        ll sum = 0;
        int mid = (l+r)>>1;
        if (x <= mid){
            sum += query(x,y,k<<1);
        }
        if (y > mid){
            sum += query(x,y,(k<<1)+1);
        }
        return sum;
    }
    
    void mchain(int x,int y,int z){  // 结点x->结点y 最短路径上所有结点加z
        while (top[x] != top[y]){
            if (dep[top[x]] < dep[top[y]])
                swap(x,y);
            modify(dfn[top[x]],dfn[x],z);
            x = fa[top[x]];
        }
        if (dep[x] > dep[y])
            swap(x,y);
        modify(dfn[x],dfn[y],z);
    }
    
    ll qchain(int x,int y){  // 查询x到y结点最短路径上所有节点的值之和
        ll ret = 0;
        while (top[x] != top[y]){
            if (dep[top[x]] < dep[top[y]])
                swap(x,y);
            ret += query(dfn[top[x]],dfn[x]);
            x = fa[top[x]];
        }
        if (dep[x] > dep[y])
            swap(x,y);
        ret += query(dfn[x],dfn[y]);
        return ret ;
    }
    
    void mson(int x,int z){ // 以x为根节点的子树内所有节点值都加上z
        modify(dfn[x],dfn[x]+siz[x]-1,z);  // 必定是连续的
    }
    
    ll qson(int x){  // 以x为根节点的子树内所有节点值之和
        return query(dfn[x],dfn[x]+siz[x]-1);
    }
    
    ll op2[maxn];
    
    void init() {
        cnt = 0;
        tim = 0;
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(top,0,sizeof(top));
        memset(w,0,sizeof(w));
        memset(v,0,sizeof(v));
        memset(fa,0,sizeof(fa));
        memset(dep,0,sizeof(dep));
        memset(siz,0,sizeof(siz));
        memset(son,0,sizeof(son));
        memset(op2,0,sizeof(op2));
    }
    
    
    int main() {
        int t;
        scanf("%d",&t);
        while (t--) {
            scanf("%d%d",&n,&m);
            init();
            for (int i = 1;i < n;i++) {
                int u,v;
                scanf("%d%d",&u,&v);
                add_edge(u,v);
            }
            build(1,n);
            dfs1(1,0);
            dfs2(1,1);
            ll sum = 0,ans = 0,dist = 0,op1 = 0;
            for (int i = 1;i <= m;i++) {
                int op;
                scanf("%d",&op);
                if (op == 1) {
                    op1++;
                    int x,w;
                    scanf("%d%d",&x,&w);
                    sum += w;
                    mchain(x,1,2);
                    dist += dep[x];
                }
                else if (op == 2) {
                    int x;
                    scanf("%d",&x);
                    ans = sum + qchain(x,1) -  dep[x] * op1 - dist - op2[x];
                    op2[x] += max(ans,0LL);
                }
                else if (op == 3) {
                    int x;
                    scanf("%d",&x);
                    ans = sum + qchain(x,1) -  dep[x] * op1 - dist - op2[x];
                    printf("%lld
    ",ans);
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    构建之法阅读笔记03
    构建之法阅读笔记01
    构建之法阅读笔记02
    周总结06
    《大道至简》第一章伪代码
    《大道至简》观后感
    【leetcode】Valid Number
    【leetcode】Maximal Rectangle
    【Unity3D】Invoke,InvokeRepeating ,Coroutine 延迟调用,周期性调用
    【leetcode】Scramble String
  • 原文地址:https://www.cnblogs.com/-Ackerman/p/13450045.html
Copyright © 2020-2023  润新知