动态树 树剖

复制代码
//题意:有一棵树,有两个指令
//      指令1:0 u delta,这棵树长出了一些果子, 即u的子树中的每个节点都会长出delta个果子
//        指令2:1 k u1 v1 ... uk vk,小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝
//        上的节点的果子数的和。注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次。
//思路:第一个指令很简单,直接dfs序区间操作就好
//      主要是第二个指令,充分发挥了树剖将路径操作转化为区间的性质
//      将路径转化许多区间之后,如果有路径重合,那么就是有区间重合,我们直接并区间就好
//      (ps:本题的查询部分的树状数组懒得写了,只写到区间并的阶段)
//
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int tr[N];
int n, q;
vector<int> mp[N];
int dep[N], siz[N], son[N], L[N], R[N], f[N], tot, top[N], idx[N];
void dfs(int x, int fa) {
    dep[x] = dep[fa] + 1;
    f[x] = fa;
    siz[x] = 1;
    for (auto y : mp[x]) {
        if (y == fa) continue;
        dfs(y, x);
        siz[x] += siz[y];
        if (siz[son[x]] < siz[y])
            son[x] = y;
    }
}//重链剖分前置处理
void dfs1(int x, int w) {
    L[x] = ++tot;
    top[x] = w;
    idx[tot] = x;
    if (son[x]) dfs1(son[x], w);
    for (auto y : mp[x]) {
        if (y == f[x] || y == son[x]) continue;
        dfs1(y, y);
    }
    R[x] = tot;
}//重链剖分,注意dfs序是这个时候的dfs序,因为之后要根据这个来更新线段树的(这道题用树状数组来整)
void modify(int x, int s) {
    for (; x <= n; x += x & (-x))
        tr[x] += s;
}
pair<int, int> edge[4 * N]; int cnt;
void query(int u, int v) {
    while(top[u]!=top[v]){
        if (dep[top[u]] < dep[top[v]]) {
            edge[++cnt] = { L[top[v]], L[v] };
            v = f[top[v]];
        }
        else {
            edge[++cnt] = { L[top[u]], L[u] };
            u = f[top[u]];
        }
    }
    if (dep[u] <= dep[v]) edge[++cnt] = { L[u],L[v] };
    else edge[++cnt] = { L[v],L[u] };
}
int way[4 * N][2]; int cnt1;//记录最终路径数
void merge() {
    sort(edge + 1, edge + 1 + cnt);
    way[0][0] = 0, way[0][1] = 0;
    for (int i = 1; i <= cnt; i++) {
        if (edge[i].first <= way[cnt1][1])
            way[cnt1][1] = max(way[cnt1][1], edge[i].second);
        else if (edge[i].first > way[cnt1][1]) {
            way[++cnt1][0] = edge[i].first;
            way[cnt1][1] = edge[i].second;
        }
    }
}//合并所有询问区间
int lookfor(int x) {
    int s = 0;
    for (; x; x -= x & (-x))
        s += tr[x];
    return s;
}
int query1() {
    int ans = 0;
    for (int i = 1; i <= cnt1; i++) {
        ans = ans - lookfor(way[i][0]);
        ans = ans + lookfor(way[i][1]);
        //cout << way[i][0] << " " << way[i][1] << endl;
    }
    return ans;
}
signed main() {
    cin >> n;
    for (int i = 1; i <= n - 1; i++) {
        int a, b; cin >> a >> b;
        mp[a].push_back(b);
        mp[b].push_back(a);
    }
    dep[0] = 0;
    dfs(1, 0);
    dfs1(1, 1);
    cin >> q;
    for (int i = 1; i <= q; i++) {
        int od, a, b, c; cin >> od;
        if (od == 0) {
            cin >> a >> b;
            //modify(R[a], b);
            //modify(L[a], -b);
        }
        else {
            cin >> c;
            for (int j = 1; j <= c; j++) {
                cin >> a >> b;
                query(a, b);
            }
            merge();
            int ans = 0;
            ans = ans + query1();
            //for (int i = 1; i <= cnt; i++)
                //cout << edge[i].first << " " << edge[i].second << endl;
            cout << ans << endl;
        }
    }
    return 0;
}
复制代码

 

posted @   Aacaod  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示