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

Posted li_wen_zhuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

以上是关于2021牛客多校9 E.Eyjafjalla(dfs序+主席树)的主要内容,如果未能解决你的问题,请参考以下文章

2021牛客暑期多校训练营9 E.Eyjafjalla

2021牛客暑期多校训练营9 E.Eyjafjalla

2021牛客暑期多校训练营9 E.Eyjafjalla

2021牛客暑期多校训练营10 E.Eyjafjalla(倍增+线段树合并)

2021牛客多校9 H Happy Number

2021牛客多校9 - Cells(推公式+NTT)