整体二分

——— 爆改cdq。

整体二分可以在 \(O((n+q)\log V \log ⁡n)\) 的时间复杂度内离线、批量处理区间第 \(k\) 排名问题。如果一个一个二分答案处理询问,那每次询问都要遍历整个数组 \(\log V\) 遍,在数据结构上修改每个位置产生 \(\log n\) 的时间,复杂度将达到 \(O(nq\log V\log n)\)。注意到每次二分 check() 会遍历整个数组并更新在数据结构上,但是只做了一次查询,效率低下。我们希望可以将有需要的询问放在一起查询,并且只记录数组中有用的位置。因此,我们可以把数组的每个位置看成一个单点修改操作,和查询操作混合之后送入 cdq 中“整体”地进行二分。

大体思路和 cdq 相同:

  • 每次递归传入操作集合 op 和区间 \([l,r]\),表示修改操作的修改值以及查询操作的答案都位于 \([l,r]\) 内;
  • 统计左区间修改全区间查询的贡献;
  • 判断查询应下放到左右哪个区间;
  • 分割操作至左右两区间,递归调用;

核心:

  • 通过 cdq 压掉值域,使树状数组可以用来维护数组下标,从而在递归时正确分配查询操作的位置;

!!! 一定要记得,给右区间的查询操作排名减去 cnt=BIT::query(o.l,o.r)!!
!!! 一定要记得,在递归函数中剪枝,否则时间复杂度会退化到 \(O(V\log n)\)!!

例题

海亮 OJ 题单

P3834 【模板】可持久化线段树 2

求给定序列的区间第 \(k\) 小。

代码
#include<iostream>
#include<vector>
using namespace std;
const int N = 2E5 + 10;
const int A = 1e9;

struct Op {
    int x, y, k;
    int id, tp;
};

int n, Q;
int ans[N];

namespace BIT {
    int sum[N];
    inline int lowbit(int x) { return x & -x; }
    inline void modify(int p, int v) {
        for(int i = p; i <= n; i += lowbit(i)) {
            sum[i] += v;
        }
    }
    inline int query(int p) {
        int res = 0;
        for(int i = p; i > 0; i -= lowbit(i)) {
            res += sum[i];
        }
        return res;
    }
    inline int query(int l, int r) {
        return query(r) - query(l - 1);
    }
}

void solve(vector<Op> &op, int l, int r) {
    if(op.empty()) return;
    if(l == r) {
        for(Op o : op) {
            if(o.tp == 0) {
                ans[o.id] = l;
            }
        }
        return;
    }
    int mid = (l + r) >> 1;
    for(Op o : op) {
        if(o.tp == 1 && o.x <= mid) BIT::modify(o.id, 1); 
    }
    vector<Op> lop, rop;
    for(Op o : op) {
        if(o.tp == 0) {
            int cnt = BIT::query(o.x, o.y);
            if(cnt >= o.k) lop.push_back(o);
            else rop.push_back({o.x, o.y, o.k - cnt, o.id, o.tp});
        } else {
            if(o.x <= mid) lop.push_back(o);
            else rop.push_back(o); 
        }
    }
    for(Op o : op) {
        if(o.tp == 1 && o.x <= mid) BIT::modify(o.id, -1);
    }
    solve(lop, l, mid);
    solve(rop, mid + 1, r);
}

vector<Op> op;

int main() {
    
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    cin >> n >> Q;
    for(int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        op.push_back({x, 0, 0, i, 1}); 
    }
    for(int i = 1; i <= Q; i++) {
        int l, r, k;
        cin >> l >> r >> k;
        op.push_back({l, r, k, i, 0});
    }
    
    solve(op, -A, A);
    
    for(int i = 1; i <= Q; i++) {
        cout << ans[i] << '\n'; 
    }
    
    return 0;
}

P2617 Dynamic Rankings

代码
#include<iostream>
#include<vector>
using namespace std;
const int N = 1E5 + 10;
const int A = 1E9; 

struct Op {
    int x, y, k;
    int id, tp;
};

int n, m;
int ans[N];

namespace BIT {
    int sum[N];
    inline int lowbit(int x) { return x & -x; }
    inline void modify(int p, int v) {
        for(int i = p; i <= n; i += lowbit(i)) {
            sum[i] += v;
        } 
    }
    inline int query(int p) {
        int res = 0;
        for(int i = p; i > 0; i -= lowbit(i)) {
            res += sum[i];
        }
        return res;
    } 
    inline int query(int l, int r) {
        return query(r) - query(l - 1);
    }
}

void cdq(vector<Op> &op, int l, int r) {
    if(op.empty()) return; 
    if(l == r) {
        for(Op o : op) {
            if(o.tp == 0) ans[o.id] = l;
        }
        return;
    }
    int mid = (l + r) >> 1;
    vector<Op> lop, rop;
    for(Op o : op) {
        if(o.tp == 0) {
            int lcnt = BIT::query(o.x, o.y);
            if(o.k <= lcnt) lop.push_back(o);
            else rop.push_back({o.x, o.y, o.k - lcnt, o.id, o.tp}); 
        } else if(o.x <= mid) {
            BIT::modify(o.id, o.tp);
            lop.push_back(o);
        } else {
            rop.push_back(o);
        }
    }
    for(Op o : op) {
        if(o.tp && o.x <= mid) {
            BIT::modify(o.id, -o.tp);
        }
    }
    cdq(lop, l, mid);
    cdq(rop, mid + 1, r);
} 

int a[N];
vector<Op> op;

int main() {
    
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        op.push_back({a[i], 0, 0, i, 1});
    }
    
    int nq = 0;
    while(m--) {
        char c;
        int l, r, x, y, k;
        cin >> c;
        if(c == 'Q') {
            cin >> l >> r >> k;
            op.push_back({l, r, k, ++nq, 0}); 
        } else {
            cin >> x >> y;
            op.push_back({a[x], 0, 0, x, -1});
            op.push_back({y, 0, 0, x, 1});
            a[x] = y;
        }
    }
    
    cdq(op, 0, A);
    
    for(int i = 1; i <= nq; i++) {
        cout << ans[i] << '\n';
    }
    
    return 0;
} 

P3332 [ZJOI2013] K大数查询

代码
#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
const int N = 5E4 + 10;

struct Op {
    int l, r, k;
    int id, tp;
};

int n, m;
int ans[N];

namespace BIT {
    int sum[N], sum2[N];
    inline int lowbit(int x) { return x & -x; }
    inline void modify(int p, int v) {
        for(int i = p; i <= n + 5; i += lowbit(i)) {
            sum[i] += v;
            sum2[i] += p * v;
        }
    } 
    inline int query(int p) {
        int res = 0;
        for(int i = p; i > 0; i -= lowbit(i)) {
            res += -sum2[i] + sum[i] * (p + 1);
        }
        return res;
    }
    inline void modify(int l, int r, int v) {
        modify(l, v);
        modify(r + 1, -v);
    }
    inline int query(int l, int r) {
        return query(r) - query(l - 1);
    }
}
    
struct Operation {
    int o, l, r, c;
} q[N];

void cdq(vector<Op> &op, int l, int r) {
    if(op.empty()) return;
    if(l == r) {
        for(Op o : op) {
            if(o.tp == 0) ans[o.id] = l;
        }
        return;
    }
    int mid = (l + r) >> 1;
    vector<Op> lop, rop; 
    for(Op o : op) {
        if(o.tp == 1) {
            if(o.k > mid) {
                BIT::modify(o.l, o.r, 1);
                rop.push_back(o);
            } else {
                lop.push_back(o); 
            }
        } else {
            int cnt = BIT::query(o.l, o.r);
            if(cnt >= o.k) {
                rop.push_back(o);
            } else {
                lop.push_back({o.l, o.r, o.k - cnt, o.id, o.tp});
            }
        }
    }
    for(Op o : op) {
        if(o.tp == 1 && o.k > mid) BIT::modify(o.l, o.r, -1); 
    }
    cdq(lop, l, mid);
    cdq(rop, mid + 1, r);
}

vector<Op> op;

signed main() {
    
    cin >> n >> m;
    
    int nq = 0;
    for(int i = 1; i <= m; i++) {
        int o, l, r, c;
        cin >> o >> l >> r >> c;
        if(o == 1) {
            op.push_back({l, r, c, 0, 1});
        } else {
            op.push_back({l, r, c, ++nq, 0});
        }
    } 
    
    cdq(op, -N, N);
    
    for(int i = 1; i <= nq; i++) {
        cout << ans[i] << '\n';
    }
    
    return 0;
} 

P1527 [国家集训队] 矩阵乘法

代码
#include<iostream>
#include<vector>
using namespace std;
const int N = 510;
const int A = 1e9;

const int Q = 6E4 + 10;

struct Op {
    int x1, y1, x2, y2, k;
    int id, tp;
};

int n, q;
int ans[Q];

namespace BIT {
    inline int lowbit(int x) { return x & -x; }
    int sum[N][N];
    inline void modify(int x, int y, int v) {
        for(int i = x; i <= n; i += lowbit(i)) {
            for(int j = y; j <= n; j += lowbit(j)) {
                sum[i][j] += v;
            }
        } 
    }
    inline int query(int x, int y) {
        int res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) {
            for(int j = y; j > 0; j -= lowbit(j)) {
                res += sum[i][j]; 
            } 
        }
        return res;
    }
    inline int query(int x1, int y1, int x2, int y2) {
        return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1);
    }
}

void cdq(vector<Op> &op, int l, int r) {
    if(op.empty()) return;
    if(l == r) {
        for(Op o : op) {
            ans[o.id] = l;
        } 
        return;
    }
    int mid = (l + r) >> 1;
    vector<Op> lop, rop;
    for(Op o : op) {
        if(o.tp == 0) {
            int cnt = BIT::query(o.x1, o.y1, o.x2, o.y2);
            if(o.k <= cnt) {
                lop.push_back(o);
            } else rop.push_back({o.x1, o.y1, o.x2, o.y2, o.k - cnt, o.id, o.tp});
        } else {
            if(o.k <= mid) {
                BIT::modify(o.x1, o.y1, 1);
                lop.push_back(o);
            } else rop.push_back(o);
        }
    } 
    for(Op o : op) {
        if(o.tp == 1 && o.k <= mid) BIT::modify(o.x1, o.y1, -1);
    }
    cdq(lop, l, mid);
    cdq(rop, mid + 1, r);
} 

vector<Op> op;

int main() {
    
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    cin >> n >> q;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            int x;
            cin >> x;
            op.push_back({i, j, 0, 0, x, 0, 1});
        } 
    }
    
    for(int i = 1; i <= q; i++) {
        int x1, y1, x2, y2, k;
        cin >> x1 >> y1 >> x2 >> y2 >> k;
        op.push_back({x1, y1, x2, y2, k, i, 0});
    }
    
    cdq(op, 0, A);
    
    for(int i = 1; i <= q; i++) {
        cout << ans[i] << '\n';
    }
    
    return 0;
}

P7424 [THUPC2017] 天天爱射击

代码
#include<iostream>
#include<vector>
using namespace std;
const int N = 2E5 + 10;
const int A = 2E5;

struct Op {
    int x, y, k;
    int id, tp;
};

int n, m;
int ans[N];

namespace BIT {
    inline int lowbit(int x) { return x & -x; }
    int sum[N];
    inline void modify(int p, int v) {
        for(int i = p; i <= A + 5; i += lowbit(i)) {
            sum[i] += v;
        }
    }
    inline int query(int p) {
        int res = 0;
        for(int i = p; i > 0; i -= lowbit(i)) {
            res += sum[i];
        }
        return res;
    } 
    inline int query(int l, int r) {
        return query(r) - query(l - 1); 
    }
}

void cdq(vector<Op> &op, int l, int r) {
    if(op.empty()) return;
    if(l == r) {
        for(Op o : op) {
            if(o.tp == 0) ans[o.id] = l;
        }
        return;
    }
    int mid = (l + r) >> 1;
    vector<Op> lop, rop;
    for(Op o : op) {
        if(o.tp == 1) {
            if(o.k <= mid) {
                BIT::modify(o.x, 1);
                lop.push_back(o);
            } else rop.push_back(o);
        } else {
            int cnt = BIT::query(o.x, o.y);
            if(o.k <= cnt) lop.push_back(o);
            else rop.push_back({o.x, o.y, o.k - cnt, o.id, o.tp}); 
        }
    }
    for(Op o : op) {
        if(o.tp == 1 && o.k <= mid) BIT::modify(o.x, -1);
    } 
    cdq(lop, l, mid);
    cdq(rop, mid + 1, r);
} 

vector<Op> op;

struct Query {
    int l, r, k;
} q[N];

int cnt[N];

int main() {
    
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> q[i].l >> q[i].r >> q[i].k;
    }
    
    for(int i = 1; i <= m; i++) {
        int x;
        cin >> x;
        op.push_back({x, 0, i, 0, 1});
    }
    
    for(int i = 1; i <= n; i++) {
        op.push_back({q[i].l, q[i].r, q[i].k, i, 0});
    }
    
    cdq(op, 1, A + 1);
    
    for(int i = 1; i <= n; i++) {
        cnt[ans[i]]++;
    }
    
    for(int i = 1; i <= m; i++) {
        cout << cnt[i] << '\n';
    }
    
    return 0;
} 

P3527 [POI2011] MET-Meteors

首先破环为链:

  • 对于操作区间(指陨石落下的区间)满足 \(l\le r\) 的情况,此时 \([l,r]\) 就是修改区间,无需额外处理;
  • 对于操作区间 \(l>r\) 的情况,将其拆为 \([1,r]\cup[l,m]\) 即可;

这样问题就转化到了链上。

我们视每次陨石为一次(区间)修改操作,每个国家的 \(p_i\) 为(查询第 \(k\) 小)一个查询操作,进行整体二分。也可以这么理解:将每次陨石拆成单块的陨石(比如一次降落三块,就拆成1+1+1);然后每个国家就是在查询落到本国家的时间第 \(p_i\) 小的陨石是第几次降落下来的。

但是一个国家的空间站是分散的,不是一个连续的区间,如何统计?

但是我们注意到每一个位置只会有一个国家的空间站,也就是说,空间站的总数为 \(m\)。对于每一个查询,我们可以枚举这个国家的所有空间站。这样即使我们枚举了所有国家,均摊时间复杂度也不会超过 \(O(m)\)

因此,在二分时:

  • 对于修改操作:使用树状数组区间维护所有 \(t_i\le mid\)(降落时间小于 \(mid\))的修改操作;
  • 对于查询操作:暴力枚举该国家的所有空间站,单点查询每一个空间站在本次递归中收获了多少陨石,直接进行累加,和 \(p_i\) 比较,决定将查询操作下方到左区间还是右区间。

*时间复杂度分析

对于每个修改操作(陨石下落),会产生从根到叶子的一条路径,长为 \(\log V\);在每个节点(二分的一个区间)处会进行两次树状数组的操作,时间复杂度 \(O(\log n)\);因此修改操作产生的总时间复杂度为 \(O(k\log n \log V)\)

对于每个查询操作,在每个节点处会产生 \(O(\frac{m}{n}\log n)\) 的时间复杂度(\(\frac{m}{n}\) 表示每个国家分到的平均空间站数量),因此总的均摊时间复杂度为 \(O(m\log n \log V)\)

综上,代码的总时间复杂度为 \(O\Big((m+k)\log n \log V\Big)\),可以通过本题。

代码

思路是重点,代码辅助理解。

#include<iostream>
#include<vector>
#define int long long
using namespace std;
const int N = 3E5 + 10;

struct Op {
    int x, y, k;
    int id, tp;
};

int n, m, k;
int a[N], p[N];
int ans[N];

vector<int> pos[N]; 

namespace BIT {
    inline int lowbit(int x) { return x & -x; }
    __int128 sum[N];
    inline void modify(int p, int v) {
        for(int i = p; i <= m + 5; i += lowbit(i)) {
            sum[i] += v;
        }
    }
    inline int query(int p) {
        __int128 res = 0;
        for(int i = p; i > 0; i -= lowbit(i)) {
            res += sum[i];
        } 
        return res;
    }
    inline void modify(int l, int r, int v) {
        modify(l, v);
        modify(r + 1, -v);
    }
}

void cdq(vector<Op> &op, int l, int r) {
    if(op.empty()) return;
    if(l == r) {
        for(Op o : op) {
            if(o.tp == 0) ans[o.id] = l; 
        }
        return;
    }
    int mid = (l + r) >> 1;
    vector<Op> lop, rop;
    for(Op o : op) {
        if(o.tp == 1) {
            if(o.id <= mid) {
                BIT::modify(o.x, o.y, o.k);
                lop.push_back(o);
            } else {
                rop.push_back(o);
            }
        } else {
            __int128 sum = 0;
            for(int i : pos[o.x]) {
                sum += BIT::query(i); 
            }
            if(o.k <= sum) lop.push_back(o);
            else rop.push_back({o.x, o.y, o.k - sum, o.id, o.tp});
        }
    } 
    for(Op o : op) {
        if(o.tp == 1 && o.id <= mid) BIT::modify(o.x, o.y, -o.k);
    } 
    cdq(lop, l, mid);
    cdq(rop, mid + 1, r);
}

vector<Op> op;

signed main() {
    
    cin >> n >> m;
    for(int i = 1; i <= m; i++) {
        cin >> a[i];
        pos[a[i]].push_back(i); 
    }
    for(int i = 1; i <= n; i++) {
        cin >> p[i];
    }
    
    cin >> k;
    for(int i = 1; i <= k; i++) {
        int l, r, k;
        cin >> l >> r >> k;
        if(l <= r) {
            op.push_back({l, r, k, i, 1});
        } else {
            op.push_back({l, m, k, i, 1});
            op.push_back({1, r, k, i, 1}); 
        }
    } 
    
    for(int i = 1; i <= n; i++) {
        op.push_back({i, 0, p[i], i, 0});
    }
    
    cdq(op, 1, k + 5);
    
    for(int i = 1; i <= n; i++) {
        if(ans[i] <= k) cout << ans[i] << '\n';
        else cout << "NIE\n"; 
    }
    
    return 0;
} 

P3242 [HNOI2015] 接水果

整体二分套扫描线。

题目要求查询 被路径* \(u\rightarrow v\) 包含的路径中,权值第 \(k\) 小的权值,考虑整体二分。

*注:本文中提到的所有路径 \(u\rightarrow v\),默认满足 \(dfn[u]<dfn[v]\);若题目输入不满足此条件,swap(x, y) 即可。

现在,经过 cdq 之后,我们需要解决一个子问题:实现以下两种操作:

  • 向集合中加入一条路径;
  • 给定路径 \(u\rightarrow v\),统计集合中被路径 \(u\rightarrow v\) 包含的路径数量;

我们发现,路径 \(u_1\rightarrow v_1\) 包含路径 \(u\rightarrow v\),本质上是对两端节点 \(u_1\)\(v_1\) 做了 dfn 序的约束。\(u_1\)\(v_1\)dfn 序的约束相互独立,这是一种二维数点问题,考虑使用扫描线求解。

\(x\)\(y\) 满足 \(lca(x,y) = x\) 时(返祖链),记 \(lca2(x, y)\) 表示 \(lca(x,y)\)\(y\) 方向的直系儿子。

具体的,对于一个修改(盘子) \(x\rightarrow y\),会对所有 包含它 的查询做出贡献:

  • \(lca(x,y)=x\) 时,会贡献到满足以下条件的查询:

    • \(dfn[x_1]\in\Big[1,dfn[lca2(x, y)]-1\Big]\)\(dfn[y_1]\in\Big[dfn[y],dfn[y]+sz[y]-1\Big]\)
    • \(dfn[x_1]\in\Big[dfn[lca2(x,y)]+sz[lca2],n\Big]\)\(dfn[y_1]\in\Big[dfn[y],dfn[y]+sz[y]-1\Big]\)
  • \(lca(x,y)\ne x\) 时(\(lca\) 不会等于 \(y\),因为 \(x\)\(dfn\) 序比 \(y\) 小),会贡献到满足以下条件的查询:

    • \(dfn[x_1]\in\Big[dfn[x],dfn[x]+sz[x]-1\Big]\)\(dfn[y_1]\in\Big[dfn[y],dfn[y]+sz[y]-1\Big]\)

我们将区间 \([l,r]\) 拆成 \([1,r]-[1,l-1]\),就可以使用扫描线做了。

struct cOp {
    int a; // 第一维的 dfn 序,是排序关键字
    int b1; // 第二维的 dfn 序,是树状数组的下标的下界 
    int b2; // 上界 
    int w; // 修改、查询的权值 
    int id; // 查询时答案存储的位置 
    int tp; // 0 表示查询,1 表示修改 
    inline bool operator<(const cOp &other) const {
        if(a != other.a) return a < other.a;
        return tp > other.tp;
    }
};
int x = o.x, y = o.y, lca = LCA::lca(x, y);
if(lca == x) {
    int lca2 = LCA::lca(x, y, 1);
    cop.push_back({0,                 dfn[y],                dfn[y] + sz[y] - 1,     1, 0, 1});
    cop.push_back({dfn[lca2],         dfn[y],                dfn[y] + sz[y] - 1,    -1, 0, 1});
    cop.push_back({dfn[y],            dfn[lca2] + sz[lca2],  n + 5,                  1, 0, 1});
    cop.push_back({dfn[y] + sz[y],    dfn[lca2] + sz[lca2],  n + 5,                 -1, 0, 1});
} else {
    cop.push_back({dfn[x],            dfn[y], dfn[y] + sz[y] - 1,  1, 0, 1});
    cop.push_back({dfn[x] + sz[x],    dfn[y], dfn[y] + sz[y] - 1, -1, 0, 1});
}
代码

思路是重点,代码辅助理解。

#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
const int N = 4E4 + 10;
const int LOGN = 16;
const int A = 1E9;

struct Edge {
    int v;
    int next;
} pool[2 * N];
int ne, head[N];

void addEdge(int u, int v) {
    pool[++ne] = {v, head[u]};
    head[u] = ne;
}

struct Op {
    int x, y, k;
    int id, tp;
};

struct cOp {
    int a; // 第一维的 dfn 序,是排序关键字
    int b1; // 第二维的 dfn 序,是树状数组的下标的下界 
    int b2; // 上界 
    int w; // 修改、查询的权值 
    int id; // 查询时答案存储的位置 
    int tp; // 0 表示查询,1 表示修改 
    inline bool operator<(const cOp &other) const {
        if(a != other.a) return a < other.a;
        return tp > other.tp;
    }
};

int n, p, q;

int dfn[N], fa[N], sz[N], dt;

namespace LCA {
    
    int dep[N], anc[N][LOGN];
    
    void init(int u, int f) {
        dep[u] = dep[f] + 1;
        anc[u][0] = f;
        dfn[u] = ++dt;
        fa[u] = f;
        sz[u] = 1;
        for(int i = 1; i < LOGN; i++) {
            anc[u][i] = anc[ anc[u][i - 1] ][i - 1];
        }
        for(int i = head[u]; i; i = pool[i].next) {
            int v = pool[i].v;
            if(v == f) continue;
            init(v, u);
            sz[u] += sz[v];
        }
    }
    
    int lca(int x, int y, bool MODE = 0) {
        if(dep[x] < dep[y]) swap(x, y);
        for(int i = LOGN - 1; i >= 0; i--) {
            if(dep[anc[x][i]] > dep[y]) x = anc[x][i]; 
        }
        if(anc[x][0] == y) {
            if(MODE) return x;
            else return y;
        }
        x = anc[x][0];
        for(int i = LOGN - 1; i >= 0; i--) {
            if(anc[x][i] != anc[y][i]) {
                x = anc[x][i];
                y = anc[y][i];
            }
        }
        if(MODE) return x;
        return anc[x][0];
    }
    
}

namespace BIT {
    inline int lowbit(int x) { return x & -x; }
    int sum[N], sum2[N];
    inline void modify(int p, int v) {
        for(int i = p; i <= n + 5; i += lowbit(i)) {
            sum[i] += v;
            sum2[i] += v * p;
        }
    }
    inline void modify(int l, int r, int v) {
        modify(l, v);
        modify(r + 1, -v);
    }
    inline int query(int p) {
        int res = 0;
        for(int i = p; i > 0; i -= lowbit(i)) {
            res += sum[i] * (p + 1) - sum2[i];
        }
        return res;
    }
    inline int query(int l, int r) {
        return query(r) - query(l - 1);
    }
}

vector<cOp> cop;
int ans[N], res[4 * N];

void cdq(vector<Op> &op, int l, int r) {
    if(op.empty()) return;
    if(l == r) {
        for(Op o : op) {
            if(o.tp == 0) {
                ans[o.id] = l;
            }
        }
        return;
    }
    int mid = (l + r) >> 1;
    cop.clear();
    for(int i = 0; i < (int)op.size(); i++) {
        Op o = op[i];
        if(o.tp == 1) {
            if(o.k <= mid) {
                int x = o.x, y = o.y, lca = LCA::lca(x, y);
                if(lca == x) {
                    int lca2 = LCA::lca(x, y, 1);
                    cop.push_back({0,                 dfn[y],                 dfn[y] + sz[y] - 1,     1, 0, 1});
                    cop.push_back({dfn[lca2],        dfn[y],                 dfn[y] + sz[y] - 1, -1, 0, 1});
                    cop.push_back({dfn[y],             dfn[lca2] + sz[lca2],     n + 5,                    1, 0, 1});
                    cop.push_back({dfn[y] + sz[y],     dfn[lca2] + sz[lca2],     n + 5,                    -1, 0, 1});
                } else {
                    cop.push_back({dfn[x],             dfn[y], dfn[y] + sz[y] - 1,  1, 0, 1});
                    cop.push_back({dfn[x] + sz[x],     dfn[y], dfn[y] + sz[y] - 1, -1, 0, 1});
                }
            } 
        } else {
            int x = o.x, y = o.y;
            cop.push_back({dfn[x], dfn[y], dfn[y], 1, i, 0});
        }
    }
    sort(cop.begin(), cop.end());
    for(int i = 0; i < (int)op.size(); i++) {
        res[i] = 0;
    }
    for(cOp o : cop) {
        if(o.tp == 1) {
            BIT::modify(o.b1, o.b2, o.w);
        } else {
            res[o.id] += BIT::query(o.b1, o.b2);
        }
    }
    for(cOp o : cop) {
        if(o.tp == 1) {
            BIT::modify(o.b1, o.b2, -o.w);
        }
    }
    vector<Op> lop, rop;
    for(int i = 0; i < (int)op.size(); i++) {
        Op o = op[i];
        if(o.tp == 0) {
            if(o.k <= res[i]) lop.push_back(o);
            else rop.push_back({o.x, o.y, o.k - res[i], o.id, o.tp});
        } else {
            if(o.k <= mid) lop.push_back(o);
            else rop.push_back(o); 
        }
    }
    cdq(lop, l, mid);
    cdq(rop, mid + 1, r); 
}

vector<Op> op;

signed main() {
    
    cin >> n >> p >> q;
    for(int i = 1; i <= n - 1; i++) {
        int u, v;
        cin >> u >> v;
        addEdge(u, v);
        addEdge(v, u);
    }
    
    LCA::init(1, 0);
    
    for(int i = 1; i <= p; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        if(dfn[a] > dfn[b]) swap(a, b);
        op.push_back({a, b, c, 0, 1});
    }
    
    for(int i = 1; i <= q; i++) {
        int u, v, k;
        cin >> u >> v >> k;
        if(dfn[u] > dfn[v]) swap(u, v);
        op.push_back({u, v, k, i, 0});
    }
    
    cdq(op, 0, A);
    
    for(int i = 1; i <= q; i++) {
        cout << ans[i] << '\n';
    }
    
    return 0;
}
posted @ 2025-01-17 11:13  Simon火腿肠  阅读(14)  评论(0编辑  收藏  举报