2021牛客多校9 E.Eyjafjalla(dfs序+主席树)

题目描述

There are n cities in the volcano country, numbered from 1 to n. The city 1 is the capital of the country, called Eyjafjalla. A large volcano is located in the capital, so the temperature there is quite high. The temperature of city i is denoted by ti.
The n cities are connected by n-1 undirected roads. The i-th road connects city ui and city vi. If ui is closer to the capital than vi , then tui>tvi. The capital has the highest temperature.
The coronavirus is now spreading across the whole world. Although the volcano country is far away from the mess, Cuber Q, the prime minister of the country, is still designing an emergency plan in case some city getting infected. Now he wants to know if a virus whose survival temperature range is [l, r] breaks out in city x, how many cities will be infected. The assumption is that, if the temperature of one city is between l and r inclusive, and it is connected to another infected city, it is infected too.

输入描述

The first line contains an integer n ( 1≤n≤10^5 ), representing the number of cities.
In each of the next n-1 lines, there are two integers u,v ( 1≤u,v≤n, u!=v) per line, denoting that a road connecting u and v.
The next line contains n space-separated integers t1,t2,……, tn (1≤ti≤10^9), representing the temperature in each city.
The next line contains one integer q (1≤q≤10^5 ), representing the number of queries.
The next q lines contain one query each line. Each query is 3 space-separated integers x,l,r (1≤x≤n,1≤l≤r≤10^9), representing a query that a virus whose survival temperature range is [l, r] breaks out in city x.

输出描述

For each query, output one integer representing the number of cities that will be infected. As a special case, if tx∈/ [l,r], please output 0.

示例

输入
4
1 2
1 3
2 4
10 8 7 6
3
1 10 10
2 6 7
3 7 10
输出
1
0
3

说明

For the third query, the virus will first land in city 3, and then infect city 1, and then infect 2, so 3 cities will be infected.

题目大意

有一棵树,每个点都有自己的温度,保证根节点1温度最高,离根节点越远温度越低。现有q个查询,每个查询要求出如果在一个节点x爆发病毒,病毒的适宜温度是[l,r],这个病毒可以传播到温度在[l,r]的临近点上,问有多少个点被感染了

题目分析

因为题目保证根节点温度最高,其余点越靠近根节点温度就越高。

因此对于一次查询来说,我们可以用倍增的方法找到离该节点最近的能够被感染的祖先节点。

然后以该节点为根,找出这颗子树中所有温度大于l的节点个数即可(因为整棵树越靠近根节点温度就越高,因此我们找出的子树根节点一定是该子树上温度最高的节点,也就是说其余的温度也一定小于r)
完成这一步我们可以用该树的dfs序建立主席树。问题就转换为了计算子树对应区间内大于l的点的个数(主席树基本操作)

代码如下
#include <iostream>
#include <cmath>
#include <cstdio>
#include <set>
#include <string>
#include <cstring>
#include <map>
#include <algorithm>
#include <stack>
#include <queue>
#include <bitset>
#define LL long long
#define ULL unsigned long long
#define PII pair<LL,LL>
#define PDD pair<double,double>
#define x first
#define y second
using namespace std;
const int N=1e5+5,INF=1e9+7;
vector<int> h[N];
int id[N],w[N],cnt;
int t[N],sz[N],fa[N][18];
int root[N],idx;
struct Node{
    int l,r;
    int size;
}tr[N*35];
void dfs(int u,int father)				//计算树的dfs序以及其倍增的fa[][]数组
{										//fa[i][j] 表示i节点向上跳2^j步后到达的节点
    fa[u][0]=father;
    id[u]=++cnt,w[cnt]=t[u],sz[u]=1;	//记录dfs序
    for(int k=1;k<=17;k++) fa[u][k]=fa[fa[u][k-1]][k-1];
    for(int v:h[u])
    {
        if(v==father) continue;
        dfs(v,u);
        sz[u]+=sz[v];
    }
}
int get(int x,int val)				//找出温度小于val的x的祖先节点
{
    for(int i=17;i>=0;i--)
        if(t[fa[x][i]]<=val) x=fa[x][i];
    return x;
}
int insert(int p,int l,int r,int x)		//主席树模板(在p版本基础上,在x位置上+1)
{
    int u=++idx;
    if(l==r)
    {
        tr[u].size=tr[p].size+1;
        return u;
    }
    tr[u]=tr[p];
    int mid=l+r>>1;
    if(x<=mid) tr[u].l=insert(tr[p].l,l,mid,x);
    else tr[u].r=insert(tr[p].r,mid+1,r,x);
    tr[u].size=tr[tr[u].l].size+tr[tr[u].r].size;
    return u;
}
int query(int p,int q,int l,int r,int k)		//查询p和q版本之间所有大于k的数的个数
{
    if(l>r) return 0;
    if(l==r&&l>=k) return tr[p].size-tr[q].size;
    int mid=l+r>>1;
    int size=tr[tr[p].r].size-tr[tr[q].r].size;
    if(mid>=k) return query(tr[p].l,tr[q].l,l,mid,k)+size;
    else return query(tr[p].r,tr[q].r,mid+1,r,k);
}
int main()
{
	int n,q;
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		h[u].push_back(v);
		h[v].push_back(u);
	}
	t[0]=INF;
	for(int i=1;i<=n;i++) scanf("%d",&t[i]);
	dfs(1,0);									//算出dfs序
    for(int i=1;i<=cnt;i++)						//建立主席树
		root[i]=insert(root[i-1],0,1e9,w[i]);
    
	scanf("%d",&q);
	while(q--)
	{
		int x,l,r;
		scanf("%d%d%d",&x,&l,&r);
		if(t[x]>r||t[x]<l)							//判断特殊值
		{
			puts("0");
			continue;
		}
		int u=get(x,r);				//以u节点为根的子树对应区间即为[id[u],id[u]+子树大小-1]
		printf("%d\n",query(root[id[u]+sz[u]-1],root[id[u]-1],0,1e9,l));
	}
 	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值