题意
n n n个员工构成一棵树,节点 i i i的年龄是 a i a_i ai
有 m m m次派对,派对发起人是 O O O,要求参与派对的人在 [ L , R ] [L,R] [L,R]内
且参与派对的人的上下级至少一人参加了派对(发起派对的人不需要)
保证父节点权值大于子节点权值
从一个点 x x x发出派对,会一直传播,也就是包含 x x x的一个连通块
这个连通块每个点的权值都在 [ L , R ] [L,R] [L,R]间
由于父亲到儿子权值一直递减,那么可以向上倍增一个刚好权值小于等于 R R R的节点
也就是在这个节点的子树内,所有儿子都满足 a i < = R a_i<=R ai<=R
于是我们进入这个节点时把 R R R插入树状数组
然后对每个节点直接查询小于等于 a i a_i ai的 L L L个数即可
离开子树时再清空树状数组上的操作即可
#include <bits/stdc++.h>
using namespace std;
const int inf = 1e9;
const int maxn = 4e5+10;
struct edge{
int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void Add(int u,int v){ d[++cnt] = (edge){ v,head[u] },head[u] = cnt; }
int n,m,dfn[maxn],fa[maxn][21],mx[maxn][21];
int sumn[maxn],ans[maxn],a[maxn],deep[maxn];
vector<int>vec[maxn];
int lowbit(int x){ return x&(-x); }
void add(int x,int val){
for( ; x<=100000 ; x+=lowbit(x) ) sumn[x] += val;
}
int ask(int x)
{
if( x==0 ) return 0;
int ans = 0;
for( ; x ; x-=lowbit(x) ) ans += sumn[x];
return ans;
}
void dfs(int u,int father)
{
fa[u][0] = father; deep[u] = deep[father]+1;
for(int i=1;i<=20;i++)
{
fa[u][i] = fa[fa[u][i-1]][i-1];
mx[u][i] = max( mx[u][i-1],mx[fa[u][i-1]][i-1] );
}
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( v==father ) continue;
mx[v][0] = a[u]; dfs(v,u );
}
}
int get(int x,int w)
{
for(int i=20;i>=0;i-- )
if( mx[x][i]<=w ) x = fa[x][i];//最多跳到1
return x;
}
void DFS(int u,int father)
{
for(int i=0;i<vec[u].size();i++) add( vec[u][i],1 );
ans[u] = ask( a[u] );
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( v==father ) continue;
DFS(v,u);
}
for(int i=0;i<vec[u].size();i++) add( vec[u][i],-1 );
}
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++)
{
int father; scanf("%d%d",&a[i],&father);
if( i==father ) continue;
Add(i,father); Add(father,i);
}
dfs(1,1); // mx[1][0] = inf;可加可不加,最多跳到1
for(int i=1;i<=m;i++)
{
int u,l,r; scanf("%d%d%d",&u,&l,&r);
int index = get( u,r );//找到最后一个小于等于的了,只能传播到这里
vec[index].push_back( l );
}
DFS(1,0);
for(int i=1;i<=n;i++)
printf("%d ",ans[i] );
}