雨天的尾巴(线段树合并 & 树上差分综合)

点击查看代码
#include <bits/stdc++.h>
#define Re register int
#define LL long long
#define ki cout << endl;

using namespace std;

//打个雨天的尾尾尾尾尾尾尾尾尾尾尾尾尾尾尾尾尾尾尾尾巴
//我复习了lca...............学习了差分....
//可真行
/*
                            1
                        2       3
                     4    5    6   7
                   8          9
点差分
    volume_up
    vol[u]++;
    vol[v]++;
    vol[lca(u, v)] --;
    vol[pre[lca(u, v)]] --;
    最后求前缀和就是给u - > v路径是都加了个一
eg:   1 2 3 4 5 6(普通差分)
    给 2 - > 5 加一
    就是 vol[2] + 1  | vol[5] + 1 | vol[6] - 1
    求前缀和就是2 -> 5都加一,而五之后没加
     给8 -> 5树上每个点都加一(树上差分)
    按上边(最上边)操作即可,因为到lca加了两个,而从lca的爹到根都也都多加了2但vol[lca(u, v)]--已经减了一次,所以再减一次就行,而lca留一次,所以只减一个
边差分
    vol[u]++;
    vol[v]++;
    vol[lca(u, v)] -= 2;
    边为点到其父亲的边
    显而易见lca到跌的边不需要,所以减两个
*/
namespace kiritokazuto {
    template <typename T> inline void in(T &x) {
        int f = 0; x = 0; char c = getchar();
        while(c < '0' || c > '9')f |= c == '-', c = getchar();
        while(c >= '0' && c <= '9')x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        x = f ? -x : x;
    }
    template <typename T> inline void ot(T x) {
        if(x < 0)putchar('-'), x = -x;
        if(x > 9)ot(x / 10); putchar(x % 10 | '0');
    }
}
/*
所以,综上所述,这个题就成了线段树合并的真真正正的板子题
但是合并时可能
出现vol[rt[x]]不为0
但是sum[rt[x]]为0的情况
即节点编号为vol[rt[x]]的救济粮数量为0(就是他这里可能是别的改了)
所以ans[x] = 0
*/
using namespace kiritokazuto;
const int maxn =  6e6 + 10, Inf = 1e5;
int len = 0;
int head[maxn];
int tot;
int n, m;
int ans[maxn];
struct QWMXQ {
    int next;
    int to;
    //int dis;
}a[maxn];
int sum[maxn];
int vol[maxn];
int root[maxn];
struct WMX {
    int lson, rson;
    #define lsp(x) tr[x].lson
    #define rsp(x) tr[x].rson
    #define mid ((l + r) >> 1)
}tr[maxn];
//差分直接给每一棵线段树上搞了,所以这里的vol[x]实际上是表示以x为根的子树的答案->从下向上合并的时候
//不是吧....我倍增20位就错,18位就对。。。。???? 
inline void Qian(int from, int to) {
    a[++len].to = to;
    //a[len].dis = dis;
    a[len].next = head[from];
    head[from] = len;
}
int f[maxn][20];//老年倍增数组
int deep[maxn];
inline void pushup(int rt) {
    if(lsp(rt) == 0) {
        sum[rt] = sum[rsp(rt)];
        vol[rt] = vol[rsp(rt)];
        return;
    }
    if(rsp(rt) == 0) {
        sum[rt] = sum[lsp(rt)];
        vol[rt] = vol[lsp(rt)];
        return;
    }
    if(sum[lsp(rt)] >= sum[rsp(rt)]) {
        sum[rt] = sum[lsp(rt)];
        vol[rt] = vol[lsp(rt)];
        //return;
    }else {
        sum[rt] = sum[rsp(rt)];
        vol[rt] = vol[rsp(rt)];
       // return;
    }
}

inline void insert(int &rt, int l, int r, int pos, int val) {
        if(!rt)rt = ++tot;
        if(l == r) {
            vol[rt] = pos;//存下该节点编号对应的真正树上的编号->动态开点
            sum[rt] += val;
            return;//结束啊!!!
        } 
        if(pos <= mid) insert(lsp(rt), l ,mid, pos, val);
        else insert(rsp(rt), mid + 1, r, pos, val);
        pushup(rt);
}


inline int merge(int rr, int tt, int l, int r) {
    if(!rr)return tt;
    if(!tt)return rr;
    if(l == r) {
        sum[rr] += sum[tt];
        return rr;//记得返回->叶子
    }
    lsp(rr) = merge(lsp(rr), lsp(tt), l, mid);
    rsp(rr) = merge(rsp(rr), rsp(tt), mid + 1, r);
    pushup(rr);//记得pushup
    return rr;
}

void dfs(int x, int fa) {
    deep[x] = deep[fa] + 1;
    f[x][0] = fa;//x 的 2 ^ 0 即 x 向上 翻 1的祖先就是他的爹
    for(Re i = 1; i <= 18; i ++)  {
        f[x][i] = f[f[x][i - 1]][i - 1];//x 的 2 ^ i 的爹是x 的 2 ^ j - 1的 2 ^ j - 1;
    }
    for(Re i = head[x], to; i ; i = a[i].next) {
        to = a[i].to;
        if(to == fa)continue;
        dfs(to, x);
    }
} 

int LCA(int a, int b) {
    if(deep[a] > deep[b])swap(a, b);//a深,把a换上来
    for(Re i = 18; i >= 0; i --) {
        if(deep[a] <= deep[f[b][i]])b = f[b][i];//拉到同一层
    }
    if(a == b)return a;
    for(Re i = 18; i >= 0; i --) {
        if(f[a][i] != f[b][i]) {
            a = f[a][i];
            b = f[b][i];
        }
    }    
    return f[a][0];
}

inline void work(int x, int fa) {
    for(Re i = head[x], to; i; i = a[i].next) {
        to = a[i].to;
        if(to == fa)continue;
        work(to, x);
        root[x] = merge(root[x], root[to], 1, Inf);
    }
    ans[x] = vol[root[x]];
    if(sum[root[x]] == 0)ans[x] = 0;
}

signed main () {
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    in(n);
    in(m);
    for(int i = 1, x ,y; i < n; i ++) {
        in(x);
        in(y);
        Qian(x, y);
        Qian(y, x);
    }
    dfs(1, 0);
    //cout << "?????????";
    for(int i = 1, x,  y, z; i <= m; i ++)  {
        in(x);
        in(y);
        in(z);
        int lca = LCA(x, y);
        insert(root[x], 1, Inf, z, 1);
        insert(root[y], 1, Inf, z, 1);
        insert(root[lca], 1, Inf, z, -1);
        insert(root[f[lca][0]], 1, Inf, z, -1);
    }
    work(1, 0);
    for(Re i = 1; i <= n; i ++) {
        cout << ans[i] << endl;
    }
    return 0;
}
posted @ 2022-04-09 07:58  kiritokazuto  阅读(43)  评论(0)    收藏  举报