示例1:
输入
10 10 7
4 2
10 4
3 2
6 10
9 2
7 3
1 4
8 2
5 3
8 10 10
2 6 2
3 6 2
4 6 4
3 10 2
8 8 10
3 10 4
2 3 2
2 6 4
1 7 10
输出
0
2
0
1
7
0
2
0
1
0
题意
给定一棵n个节点的有根数,第i个点的编号是i。
有m次询问,每次询问给出l,r,x,求有多少点编号的二元组(i,j)满足 l<=i<j<=r 且 i 和 j 的最近公共祖先是节点x。
分析:
- 这个题有矩阵乘法相关规约,难以polylog解决, 考虑根号算法。 以下复杂度分析中n和m都写做n。 这个询问转化一下,
就是查询x子树在 [l,r] 内的点个数平方, 减去x的每个儿子的子树在 [l,r] 内的点个数平方。 前者平凡,考虑后者:
这个是一个点所有儿子子树信息的查询,我们可以对原树轻重链剖分,用莫队维护每个点所有轻儿子的答案,这样每次修改沿着重链向上面跳,只会影响O(logn)个位置,然后再求重儿子的答案,总时间复杂度O(n√n
logn)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int id[maxn];
struct Qr{
int l, r, p;
bool operator < (const Qr &o) const{
return id[l] == id[o.l] ? (id[l] & 1 ? r > o.r : r < o.r) : id[l] < id[o.l];
}
};
vector<Qr> qr[maxn];
vector<int> G[maxn];
pii pi[maxn];
int siz[maxn], son[maxn], vis[maxn];
ll ans[maxn], cnt[maxn];
int n, m, rt, tot;
const int maxs = 1 << 20;
char buf[maxs], *p1 = buf, *p2 = buf;
inline char fr(){
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, maxs, stdin)) == p1 ? -1 : *p1++;
}
#define gc fr()
inline void read(int &x){
char ch; while(!isdigit(ch = gc)); x = ch ^ 48;
while(isdigit(ch = gc)) x = x * 10 + (ch ^ 48);
}
#define lowb(x) ((x)&(-(x)))
struct BIT{
int c[maxn];
void add(int x, int v){
while(x <= n) c[x] += v, x += lowb(x);
}
int sum(int x){
int ret = 0;
while(x > 0) ret += c[x], x -= lowb(x);
return ret;
}
int sum(int l, int r){
return sum(r) - sum(l - 1);
}
} bit;
void dfs1(int u, int f){
siz[u] = 1, son[u] = 0;
for(auto &v : G[u]){
if(v == f) continue;
dfs1(v, u);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
void cal(int u, int f, int flg){
bit.add(u, flg);
for(auto &v : G[u]){
if(v == f || vis[v]) continue;
cal(v, u, flg);
}
}
void add(int u, int f, int bel){
pi[++tot] = {u, bel};
for(auto &v : G[u]){
if(v == f) continue;
add(v, u, bel);
}
}
inline ll pw(ll x){
return x * x;
}
void dfs2(int u, int f, int flg){
for(auto &v : G[u]){
if(v == f || v == son[u]) continue;
dfs2(v, u, -1);
}
if(son[u]) dfs2(son[u], u, 1), vis[son[u]] = 1;
for(auto &q : qr[u]){
ans[q.p] -= pw(bit.sum(q.l, q.r));
}
cal(u, f, 1);
for(auto &q : qr[u]){
ans[q.p] += pw(bit.sum(q.l, q.r));
}
tot = 0;
for(auto &v : G[u]){
if(v == f || v == son[u]) continue;
add(v, u, v);
}
sort(pi + 1, pi + 1 + tot);
for(auto &q : qr[u]){
q.l = lower_bound(pi + 1, pi + 1 + tot, make_pair(q.l, 0)) - pi;
q.r = upper_bound(pi + 1, pi + 1 + tot, make_pair(q.r, inf)) - pi - 1;
if(q.l > q.r) q.l = 1, q.r = 0;
}
int B = sqrt(tot);
for(int i = 1; i <= tot; ++i){
id[i] = (i - 1) / B + 1;
}
sort(qr[u].begin(), qr[u].end());
int l = 1, r = 0; ll cur = 0;
auto add = [&](int x){
cur += cnt[pi[x].second]++ << 1 | 1;
};
auto del = [&](int x){
cur -= --cnt[pi[x].second] << 1 | 1;
};
for(auto &q : qr[u]){
while(l > q.l) add(--l);
while(r < q.r) add(++r);
while(l < q.l) del(l++);
while(r > q.r) del(r--);
ans[q.p] -= cur;
}
for(auto &v : G[u]){
if(v == f || v == son[u]) continue;
cnt[v] = 0;
}
if(son[u]) vis[son[u]] = 0;
if(flg == -1) cal(u, f, -1);
}
int main(){
read(n), read(m), read(rt);
for(int i = 1; i < n; ++i){
int u, v; read(u), read(v);
G[u].pb(v), G[v].pb(u);
}
for(int i = 1; i <= m; ++i){
int l, r, x; read(l), read(r), read(x);
qr[x].pb({l, r, i});
}
dfs1(rt, 0);
dfs2(rt, 0, 1);
for(int i = 1; i <= m; ++i){
ans[i] >>= 1;
printf("%lld\n", ans[i]);
}
return 0;
}