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\n",ans);
            }
        }
    }
    return 0;
}

 

posted @ 2020-08-07 01:11  _Ackerman  阅读(256)  评论(0编辑  收藏  举报