牛客2021暑期训练9-E-Eyjafjalla

牛客2021暑期训练9-E-Eyjafjalla

题目链接

题意

给定一个以 1 1 1 为根的有根树,孩子的点权小于父亲的点权。多次询问,每次询问包含 x x x 节点的权值范围为 [ l , r ] [l, r] [l,r] 的极大连通块的大小

题解

病毒传播可以看作两个阶段,第一个阶段先上升到可感染的最高的一个节点 r t rt rt,第二阶段感染 r t rt rt 的子树中所有温度大于 l l l 的城市

第一阶段可以通过倍增法求得 r t rt rt

第二阶段相当于在 r t rt rt 的子树中查询权值大于 l l l 的节点个数,根据每个节点的 d f s dfs dfs 序建立线段树,存下区间最大最小值,然后在线段树上查询小于 l l l 的节点个数, r t rt rt 子树的节点数减去即可

在这里插入图片描述

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int n,q,cnt,tot;
int T[N],L[N],R[N],f[N],fa[N],st[N][29],siz[N];
int nxt[N*2],ver[N*2],head[N];
struct node
{
	int l,r,mxt,mit,sz;
}t[N*4];
void Add(int u,int v)
{
	nxt[++cnt]=head[u];
	head[u]=cnt;
	ver[cnt]=v;
}
void Getdfn(int u,int fat)
{
	L[u]=++tot; //L、R为dfs序
	f[tot]=u; //f是dfs序与原节点对应关系
	fa[u]=fat; //fa用来做倍增 
	siz[u]=1; //siz为子树大小
	for(int i=head[u];i;i=nxt[i])
	{
		int v=ver[i];
		if(v==fat) continue;
		Getdfn(v,u);
		siz[u]+=siz[v];
	}
	R[u]=tot;
}
void ST() //倍增 
{
	for(int i=1;i<=n;i++)
		st[i][0]=fa[i];
	for(int j=1;j<=20;j++)
		for(int i=1;i<=n;i++)
			st[i][j]=st[st[i][j-1]][j-1];
}
void Make_Tree(int u,int l,int r) //建树 
{
	t[u].l=l; t[u].r=r;
	if(l==r)
	{
		t[u].sz=1;
		t[u].mxt=T[f[l]]; //记录区间最大最小,f为对应节点 
		t[u].mit=T[f[l]];
		return;
	}
	int mid=(l+r)>>1;
	Make_Tree(u<<1,l,mid);
	Make_Tree((u<<1)|1,mid+1,r);
	t[u].sz=t[u<<1].sz+t[(u<<1)|1].sz;
	t[u].mxt=max(t[u<<1].mxt,t[(u<<1)|1].mxt);
	t[u].mit=min(t[u<<1].mit,t[(u<<1)|1].mit);
}
int Find_Max(int u,int maxn) //向上倍增找rt 
{
	for(int i=20;i>=0;i--)
		if(T[st[u][i]]<=maxn)
			u=st[u][i];
	return u;
}
int Query(int u,int l,int r,int al,int ar,int e) //查询温度小于l的个数 
{
	if(al<=l&&r<=ar) //在区间里满足下面可直接返回,不满足一直向下找 
	{
		if(t[u].mxt<e) return (r-l+1);
		if(t[u].mit>=e) return 0;
	}
	int mid=(l+r)>>1;
	if(ar<=mid) return Query(u<<1,l,mid,al,ar,e);
	else if(al>mid) return Query((u<<1)|1,mid+1,r,al,ar,e);
	else return Query(u<<1,l,mid,al,ar,e)+Query((u<<1)|1,mid+1,r,al,ar,e);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		Add(u,v);
		Add(v,u);
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&T[i]);
	T[0]=1<<30;
	Getdfn(1,0);
	ST();
	Make_Tree(1,1,n);
	scanf("%d",&q);
	while(q--)
	{
		int x,l,r;
		scanf("%d%d%d",&x,&l,&r);
		if(T[x]<l||T[x]>r)
		{
			printf("0\n");
			continue;
		}
		int rt=Find_Max(x,r);
		int tot=Query(1,1,n,L[rt],R[rt],l);
		printf("%d\n",siz[rt]-tot);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值