A National Pandemic (思惟 + 樹鏈剖分模版)

題目連接:https://ac.nowcoder.com/acm/contest/5672/Cios

 

題目大意:ui

一棵無根樹,每一個點都有一個點權 f(x),其初值均爲 0,有三種操做。
操做1:對全部的 y,修改f(y) 爲 w - dist(x,y)
操做2:修改 f(x) 爲 min(f(x),0)
操做3:查詢 f(x)spa

 

想法:code

對於操做一: w - dist(x,y) = w - dep[x] - dep[y] + 2 * dep[lca]   對於 dep[x] 和 dep[y] 咱們能夠直接獲得,因此如今問題轉化爲如何維護 2 * dep[lca]blog

 

 

因此對於操做一以後對於當前的 x ,咱們的答案就是 ans = sum【表明全部的w的和】 + qchain(x,1)【1->x的修改次數,即cnt】 -  dep[x] * op1 【op1 表明進行了幾回操做一】- dist 【全部的dep[y]的和】- op2[x] 【操做二取最小值致使的差值】get

 

由於操做二的要麼是f[x] 要麼是 0,咱們不妨就維護一個最大值,每次變化的最終結果就是以前的值減去的最大的那個string

 

其他的基本上就是樹鏈剖分的板子了it

 

 

#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;
}
相關文章
相關標籤/搜索