2020-2021 ACM-ICPC Brazil Subregional Programming Contest E. Party Company(倍增+树状数组)

题意

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] );
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值