整体二分
——— 爆改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)\)!!
例题
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;
}