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

Posted issue是fw

tags:

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

LINK

考虑在点 x x x处爆发 [ l , r ] [l,r] [l,r]的瘟疫且 x ∈ [ l , r ] x\\in[l,r] x[l,r]

考虑 x x x往上延伸,节点温度不断上升,所以往上的节点不需要考虑下界 l l l的限制

显然可以倍增到一个深度最浅的节点 v v v满足 t v < = r t_v<=r tv<=r,再往上的节点都不符合要求

又因为 v v v子树内的节点温度都小于 t v t_v tv,显然不需要考虑上界 r r r的限制

问题转化为,求出 v v v的子树内有多少节点的温度大于等于 l l l

这个东西可以线段树合并保存每个节点的子树内温度为下标的权值线段树即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
const int N = 1e7+10;
vector<int>vec[maxn];
int n,top,x[maxn],l[maxn],r[maxn],t[maxn],li[N],fa[maxn][22];
int get(int x,int mx)
{
	for(int i=20;i>=0;i--)
		if( t[ fa[x][i] ]<=mx )	x = fa[x][i];
	return x;
}
int sum[N],ls[N],rs[N],root[maxn],id;
int merge(int x,int y,int l,int r)
{
	if( !x || !y )	return x|y;
	int p = ++id,mid = l+r>>1;
	if( l==r )
	{
		sum[p] = sum[x]+sum[y];
		return p;
	}
	ls[p] = merge( ls[x],ls[y],l,mid );
	rs[p] = merge( rs[x],rs[y],mid+1,r );
	sum[p] = sum[ls[p]]+sum[rs[p]];
	return p;
}
int ask(int rt,int l,int r,int L,int R)
{
	if( !rt || l>R || r<L )	return 0;
	if( l>=L && r<=R )	return sum[rt];
	int mid = l+r>>1;
	return ask( ls[rt],l,mid,L,R )+ask( rs[rt],mid+1,r,L,R );
}
void update(int &rt,int l,int r,int val)
{
	if( !rt )	rt = ++id;
	if( l==r && l==val )	{ sum[rt]++; return; }
	int mid = l+r>>1;
	if( val<=mid )	update( ls[rt],l,mid,val );
	else update( rs[rt],mid+1,r,val );
	sum[rt] = sum[ls[rt]]+sum[rs[rt]];
}
void dfs(int u,int father)
{
	update( root[u],1,top,t[u] );
	fa[u][0] = father;
	for(int i=1;i<=20;i++)	fa[u][i] = fa[fa[u][i-1]][i-1];
	for(auto v:vec[u] )
	{
		if( v==father )	continue;
		dfs( v,u );
		root[u] = merge( root[u],root[v],1,top );
	}
}
int main()
{
	ios::sync_with_stdio( false );
	cin >> n;
	for(int i=1;i<n;i++)
	{
		int l,r; cin >> l >> r;
		vec[l].push_back( r ); vec[r].push_back( l );
	}
	for(int i=1;i<=n;i++)	cin >> t[i], li[++top] = t[i];
	int q; cin >> q;
	for(int i=1;i<=q;i++)	cin >> x[i] >> l[i] >> r[i];
	for(int i=1;i<=q;i++)	li[++top] = l[i], li[++top] = r[i];
	sort( li+1,li+1+top );
	top = unique( li+1,li+1+top )-li-1;
	for(int i=1;i<=n;i++)
		t[i] = lower_bound( li+1,li+1+top,t[i] )-li;
	for(int i=1;i<=q;i++)
	{
		l[i] = lower_bound( li+1,li+1+top,l[i] )-li;
		r[i] = lower_bound( li+1,li+1+top,r[i] )-li;		
	}
	dfs( 1,1 );
	for(int i=1;i<=q;i++)
	{
		int u = get( x[i],r[i] );//刚好小于等于r的那个节点
		if( t[ x[i] ]>r[i] || t[ x[i] ]<l[i] )	cout << 0 << endl;
		else	cout << ask( root[u],1,top,l[i],r[i] ) << endl;	
	}
} 

以上是关于2021牛客暑期多校训练营10 E.Eyjafjalla(倍增+线段树合并)的主要内容,如果未能解决你的问题,请参考以下文章

2021牛客暑期多校训练营4

2021牛客暑期多校训练营9

2021牛客暑期多校训练营3

2021牛客暑期多校训练营5

2021牛客暑期多校训练营1

2021牛客暑期多校训练营9,签到题HE