牛客多校第九场 E Eyjafjalla(树上倍增+主席树)

牛客多校第九场 E Eyjafjalla(树上倍增+主席树)

题目链接:E Eyjafjalla

题目大意:

给你一颗树,每个节点代表一个城市并且有各自的温度,其中节点 \(1\) 作为城市首都,温度最高,特别的是,每个子节点的温度小于父节点的温度,即温度从节点 \(1\) 向下递减。
我们有 \(q\) 组询问,魅族询问给出 \(x,l,r\) 分别表示在城市 \(x\) 爆发病毒,并且病毒在温度 \([l,r]\) 之间才能够存活,对于每个询问给出有多少个城市会被感染。
我们拿一组样例来直观的看一下:
image

思路解析:

对于本题,聪(ju)明(ruo)的小飞龙很快就想出了一个 \(O(n*q)\) 暴力的方法:
我们可以对每个询问做一遍 \(DFS\) ,但是当小飞龙看到 1<=n<=10^5 1<=q<=10^5 的时候,果断pass
然后,聪(ju)明(ruo)的小飞龙很快就想出了一个优化暴力的方法:
我们可以预处理出每个节点的子树的温度下限,当询问到这颗树的时候,就可以判断询问的温度下限是否小于子树的温度下限,如果小于,就可以直接加上这棵树的节点数量,否则就需要继续向下递归。而询问的温度上限我们可以在树上往它的父亲跳,直到恰好能够不超过温度上限。
有了思路的小飞龙很快码出了暴力,欣喜若狂的他交了一发,毫无悬念的TEL掉了。
但是,不(da)放(ju)弃(ruo)的小飞龙再次想出了解决办法:

以下内容为本题正解:

对于每次查询的上限,我们可以采用树上倍增的方法,向上找到恰好不超过温度上限的祖先节点,然后显然答案一定在这棵子树中求得,而这棵树有这一个很好的性质,温度由上向下递减,所以我们可以利用这一性质,来算答案。对于答案的求得,我们需要计算子树中在 \([l,r]\) 温度范围内的点,稍微转化一下,我们可以先求子树中小于 温度下限 \(l\) 的个数,然后用子树的大小减去小于 \(l\) 的个数就是我们所要求的答案。
那么我们怎么来求呢?
一说到子树,我们就很容易想到 \(DFS\) 序,因为一个树的 \(DFS\) 序中,一个节点的子树是连续的一段,可以配合其他数据结构来对区间进行维护来达到对子树的操作与查询。所以我们可以用主席树来维护一个 \(DFS\) 序,这样我们就可以用主席树来维护查询子树中小于等于某个数 \(k\) 的数量。通过这样的转化,我们就将本题转化为了 树上倍增求符合条件的祖先节点+主席树维护区间内小于等于 \(k\) 的个数

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e5+5;
int n,m;
int e[maxn],h[maxn],nex[maxn],id;
int t[maxn],depth[maxn],f[maxn][21],sz[maxn];
int in[maxn],out[maxn],dfn[maxn],tim,top;
void add(int x,int y){
	 e[++id]=y;
	 nex[id]=h[x];
	 h[x]=id;
}
void dfs(int u,int fa){
	in[u]=++tim;
	dfn[++top]=u;
	sz[u]=1;
	f[u][0]=fa;
    for(int i=1;i<20;i++)
        f[u][i]=f[f[u][i-1]][i-1];
	for(int i=h[u];i;i=nex[i]){
		int j=e[i];
		if(j==fa)continue;
		dfs(j,u);
		sz[u]+=sz[j];
	}
	out[u]=tim;
}
int findfa(int u,int tmp){
	for(int i=19;i>=0;i--){
		if(f[u][i]&&t[f[u][i]]<=tmp)
    		u=f[u][i];
	}
	return u;
}
vector<int >v;
int getid(int x){
	return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
struct node{
	int l,r,sum;
}hjt[maxn*40];
int cnt,root[maxn];
void insert(int l,int r,int pre,int &now,int p){
	hjt[++cnt]=hjt[pre];
	now=cnt;
	hjt[now].sum++;
	if(l==r)return ;
	int mid=(l+r)>>1;
	if(p<=mid)insert(l,mid,hjt[pre].l,hjt[now].l,p);
	else insert(mid+1,r,hjt[pre].r,hjt[now].r,p);
}
int query(int l,int r,int pre,int now,int k){
	if(l==r)return hjt[now].sum-hjt[pre].sum;
	int mid=(l+r)>>1;
	if(k<=mid)return query(l,mid,hjt[pre].l,hjt[now].l,k);
	else return hjt[hjt[now].l].sum-hjt[hjt[pre].l].sum+query(mid+1,r,hjt[pre].r,hjt[now].r,k);
}
void solved(int x,int l,int r){
	int rt=findfa(x,r);
	if(t[rt]>r||t[rt]<l){
		cout<<0<<endl;
		return;
	}
	int k=upper_bound(v.begin(),v.end(),l-1)-v.begin();
	if(!k){
		cout<<sz[rt]<<endl;
		return ;
	}
	cout<<sz[rt]-query(1,v.size(),root[in[rt]-1],root[out[rt]],k)<<endl;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++){
		cin>>t[i];
		v.push_back(t[i]);
	}
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	for(int i=1;i<=n;i++){
		insert(1,v.size(),root[i-1],root[i],getid(t[dfn[i]]));
	}
	cin>>m;
	while(m--){
		int x,l,r;
		cin>>x>>l>>r;
		if(t[x]>r||t[x]<l){
			cout<<0<<endl;
			continue;
		}
		solved(x,l,r);
	}


}
posted @ 2021-08-18 13:14  不会飞的小飞龙  阅读(38)  评论(0编辑  收藏  举报
Live2D