2021牛客暑期多校训练营9E Eyjafjalla (倍增,dfs序,主席树)

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021牛客暑期多校训练营9E Eyjafjalla (倍增,dfs序,主席树)相关的知识,希望对你有一定的参考价值。

E Eyjafjalla

题意:

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

思路:

  • 病毒传播可以看作两个阶段,第一个阶段先上升到最高的一个节点p(p的温度大于r),第二阶段感染p的子树中所有温度大于l的城市。
  • 第一阶段可以通过倍增法求得p
  • 第二阶段相当于在p的子树中查询权值大于l的节点个数,根据每个节点的dfs序建立可持久化线段树, 然后在线段树上查询即可。时间复杂度O(n log n)。
#include<bits/stdc++.h>
using namespace std;
const int maxn  = 1e5+10;

int n;
vector<int>G[maxn];
int a[maxn];

//倍增找p
int fa[maxn], up[maxn][30];
void getf(){
	for(int i = 1; i <= n; i++)up[i][0]=fa[i];
	for(int j = 1; j <= 20; j++)
		for(int i = 1; i <= n; i++)
			up[i][j] = up[up[i][j-1]][j-1];//i向上2^j层的祖先
}

//预处理dfs序[dfn,low]
int clk, dfn[maxn], low[maxn], ord[maxn];
void pre(int x, int f){
	fa[x] = f;
	dfn[x] = ++clk;
	ord[clk] = x;
	for(int to: G[x]){
		if(to != f)pre(to,x);
	}
	low[x] = clk;
}

//主席树
int tot, rt[maxn<<5], lch[maxn<<5], rch[maxn<<5], sum[maxn<<5];
void modify(int x, int f, int l, int r, int pos){  //v[pos]+=1;
	if(l==r){
		sum[x] = sum[f]+1;
		return ;
	}
	int mid = l+r>>1;
	if(pos <= mid){
		rch[x] = rch[f];//直接用上一棵树的节点
		lch[x] = ++tot; //需要新建节点
		modify(lch[x], lch[f], l, mid, pos);
	}else{
		lch[x] = lch[f];
		rch[x] = ++tot;
		modify(rch[x], rch[f], mid+1,r,pos);
	}
	sum[x] = sum[lch[x]]+sum[rch[x]];
}
int query(int p, int l, int r, int ll, int rr){//查询版本线段树值域范围在[l,r]的节点个数
	if(p==0)return 0;
	if(ll<=l && r<=rr)return sum[p];
	if(l>rr || r<ll)return 0;
	int mid = (l+r)>>1, ans = 0;
	ans += query(lch[p], l, mid, ll, rr);
	ans += query(rch[p], mid+1, r, ll, rr);
	return ans;
}


int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i = 1; i < n; i++){
		int u, v;  cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	for(int i = 1; i <= n; i++)cin>>a[i];  a[0] = 1e9+7;
	pre(1,0);
	getf();
	for(int i = 1; i <= n; i++){//建立n个版本的值域线段树
		rt[i] = ++tot;
		modify(rt[i], rt[i-1], 1, 1e9, a[ord[i]]);
	}
	int q;  cin>>q;
	while(q--){
		int x, l, r;  cin>>x>>l>>r;
		if(a[x]<l || a[x]>r){ cout<<"0\\n"; continue; }
		for(int i = 20; i >= 0; i--){//先上升到最高点,满足刚好a[x]>r
			if(a[up[x][i]]<=r)x=up[x][i];
		}
		//查询子树[dfn[x]-1, low[x]]中,权值在[l,r]中的节点个数
		cout<<query(rt[low[x]], 1, 1e9, l,r)-query(rt[dfn[x]-1], 1,1e9, l, r)<<"\\n";
	}
	return 0;
}

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

2021牛客暑期多校训练营4

2021牛客暑期多校训练营9

2021牛客暑期多校训练营3

2021牛客暑期多校训练营5

2021牛客暑期多校训练营1

2021牛客暑期多校训练营6