题目链接:芝麻开门
题意:给你一棵树,有q 个询问,问 u , v 路径上 边权 <= k 的边的数量;
思路:用主席树 维护 边权,代表 u 节点 到 根节点的信息,故有 c[lson[u]] + c[lson[v]] - 2 * c[lson[LCA]]; (这个画一画就能理解为什么和点权不一样)
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs i << 1 + 1
#define INT(t) int t; scanf("%d",&t)
using namespace std;
const int maxn = 1e5 + 10;
const int M = maxn * 40;
int T[maxn];///T[i]表示第i颗线段树的顶结点
int lson[M],rson[M],c[M];///C[i]就代表第i个点的权值,即上述权值线段树的sum
int X[maxn]; /// lson[i], rson[i] 表示第 i 个点的左儿子,右儿子是谁
int tot = 0,en; /// tot 用于动态开点
int head[maxn];
struct xx{
int u,v,w,nex;
xx(int u = 0,int v = 0,int w = 0,int nex = 0):
u(u),v(v),w(w),nex(nex){}
}edge[maxn * 2];
int coun = 0;
///倍增算法
int dep[maxn]; ///深度
int fa[maxn][25]; /// fa[i][j] 表示 i 节点的第 2^j 的祖先
int n,m,q; ///其中 m = log(n * 1.0) / log(2.0); n 为总数,编号为 1 ~ n;
int update(int rt,int pos,int val){
int newrt = tot ++,tmp = newrt;
c[newrt] = c[rt] + val;
int l = 1,r = en;
while(l < r){
int mid = (l + r) >> 1;
if(pos <= mid){
lson[newrt] = tot ++;
rson[newrt] = rson[rt];
newrt = lson[newrt];
rt = lson[rt];
r = mid;
}
else {
rson[newrt] = tot ++;
lson[newrt] = lson[rt];
newrt = rson[newrt];
rt = rson[rt];
l = mid + 1;
}
c[newrt] = c[rt] + val;
}
return tmp;
}
void dfs(int f,int s){
dep[s] = dep[f] + 1;
for(int j = head[s];~j ;j = edge[j].nex){
int v = edge[j].v;
if(v != f){
fa[v][0] = s;
for(int i = 1;i <= m;i ++)
fa[v][i] = fa[fa[v][i-1]][i-1];
int pos = lower_bound(X + 1,X + 1 + en,edge[j].w) - X;
T[v] = update(T[s],pos,1);
dfs(s,v);
}
}
}
int lca(int u,int v){
if(dep[u] < dep[v]) swap(u,v); ///u 在下面
int d = dep[u] - dep[v];
for(int i = 0; (1 << i) <= d;i ++) ///移动到相同深度
if((1 << i) & d) ///(1<<i)&f找到f化为2进制后1的位置,移动到相应的位置
u = fa[u][i]; ///比如f=5(101),先移动2^0祖先,然后再移动2^2祖先
if(u != v){
for(int i = m;i >= 0;i --)
if(fa[u][i] != fa[v][i]) ///从最大祖先开始,判断a,b祖先,是否相同
u = fa[u][i], v = fa[v][i]; ///如不相同,a b同时向上移动2^j
u = fa[u][0];
}
return u;
}
int build(int l,int r){
int rt = tot ++;
c[rt] = 0;
if(l != r){
int mid = (l + r) >> 1;
lson[rt] = build(l,mid);
rson[rt] = build(mid + 1,r);
}
return rt;
}
int query(int u,int v,int k){
int l = 1,r = en;
int lrt = T[u],rrt = T[v];
int LCA = lca(u,v),LCArt = T[LCA];
int faLCArt = T[fa[LCA][0]];
int ans = 0;
while(l < r){
int mid = (l + r) >> 1;
if(X[mid] <= k){ ///简单画一画就理解了
ans += c[lson[rrt]] + c[lson[lrt]] - c[lson[LCArt]] * 2;
l = mid + 1;
rrt = rson[rrt]; lrt = rson[lrt];
LCArt = rson[LCArt];
faLCArt = rson[faLCArt];
}
else {
r = mid;
lrt = lson[lrt]; rrt = lson[rrt];
LCArt = lson[LCArt];
faLCArt = lson[faLCArt];
}
}
if(l == r){
if(k >= X[l])
ans += c[rrt] + c[lrt] - c[lson[LCArt]] * 2;
}
return ans;
}
int main() {
scanf("%d%d",&n,&q);
m = log(n*1.0) / log(2.0);
int cnt = 0;
for(int i = 0;i < maxn;++ i) head[i] = -1;
for(int i = 1;i < n;++ i){
int u,v,w; scanf("%d%d%d",&u,&v,&w);
edge[coun] = xx(u,v,w,head[u]);
head[u] = coun ++;
edge[coun] = xx(v,u,w,head[v]);
head[v] = coun ++;
X[++ cnt] = w;
}
sort(X + 1,X + 1 + cnt);
en = unique(X + 1,X + 1 + cnt) - X - 1;
T[0] = build(1,en);
dep[0] = -1;
dfs(0,1);
while(q --){
int u,v,k; scanf("%d%d%d",&u,&v,&k);
printf("%d\n",query(u,v,k));
}
return 0;
}