题意,有一个有根树,每个加点都有一个val,每条边都有一个权值,如果u是v的子树,并且dis(u,v)<=val(u),那么认为v可以控制u,问每个点可以控制多少个点。
有两种做法一种nlogn线段树区间更新。还有一种树上区间更新的神奇算法。
1:nlogn:
线段树维护的是当前栈里元素的可以控制节点的个数。
建立两个栈,一个放栈里元素的下标,一个放栈里元素到他所有父亲的距离,每遍历到一个点,先二分查找它可以到达的最远父亲。然后更新这个父亲在栈里的下标到栈顶-1,全部加1,然后每个节点的答案就是它出栈的时候它在线段树里面的值。
2:n
对于线性的数据,l到r区间更新可以看做在l-1处减1,r+1处加1,那么在所有的更新操作进行完之后,sum[i] += sum[i-1];
这题是一个树,如果在树上需要对l到r这一段全部加上一个数,那么让l的父亲的减去这个数,r加上这个数,由深度排列,sum[i] += sum[(i的所有儿子)];
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 2e5+100; struct edge{ int to,w; }; vector<edge> G[N]; int val[N]; ll que[N]; ll dis[N]; int sum[N<<2]; int ans[N]; int n; void pushdown(int rt){ if(sum[rt]){ sum[rt<<1]+= sum[rt]; sum[rt<<1|1] += sum[rt]; sum[rt] = 0; } } void update(int L,int R,int l,int r,int rt,int c){ if(l>= L && r <= R){ sum[rt] += c; return; } pushdown(rt); int mid = (r+l)>>1; if(mid >= L) update(L,R,l,mid,rt<<1,c); if(mid < R) update(L,R,mid+1,r,rt<<1|1,c); } int query(int x,int l,int r,int rt){ if(l == r){ return sum[rt]; } pushdown(rt); int mid = (r+l)>>1; if(mid>= x)return query(x,l,mid,rt<<1); else return query(x,mid+1,r,rt<<1|1); } void update2(int x,int l,int r,int rt){ if(l == r){ sum[rt]= 0; return ; } pushdown(rt); int mid = (r+l)>>1; if(mid >= x) update2(x,l,mid,rt<<1); else update2(x,mid+1,r,rt<<1|1); }