[Ynoi2011] 成都七中

一、题目

点此看题

二、解法

建立原树的点分树,由于性质:对于树上任意一个连通块,存在一个连通块的点,使得整个连通块都在这个点的子树中。对于询问 \((l,r,x)\),暴力访问点分树上的祖先,可以找到最浅的和 \(x\) 在同一个连通块的点 \(p\),那么这个询问可以等效成 \((l,r,p)\),并且对询问有影响的点全部在 \(p\) 的子树内。

对于所有的 \(p\) 分别处理,考虑 \(p\) 的子树中,每个点 \(u\) 可以写成 \((mn,mx,col)\) 的三元组,分别代表 \(u\)\(p\) 在原树路径上的最小编号 \(/\) 最大编号,和点 \(u\) 的颜色。那么问题变成了有多少种颜色满足 \(l\leq mn\leq mx\leq r\)

把询问和三元组一起离线下来,按照第一维从大到小排序。对于每种颜色,我们维护其最小的 \(mx\),显然把最小的 \(mx\) 放进树状数组中不重不漏,询问就可以直接查询了。

时间复杂度 \(O(n\log^2 n)\)

三、总结

本题最妙的一步其实是建立点分树。在本题的语境下,本质上还是要暴力枚举询问中的 \(x\) 这一维,但是点分树利用了其子树和为 \(O(n\log n)\) 的性质,优化了这一枚举的过程,这就是结构的魅力!类似的题目还有:Ridiculous Netizens

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 100005;
const int inf = 0x3f3f3f3f;
#define pb push_back
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,rt,sz,a[M],b[M],c[M],ans[M],siz[M],mx[M],vis[M];
struct node{int x,y,z,tp;};
vector<node> s[M],f[M];vector<int> g[M];
void find(int u,int fa)
{
	siz[u]=1;mx[u]=0;
	for(int v:g[u]) if(v^fa && !vis[v])
	{
		find(v,u);
		siz[u]+=siz[v];
		mx[u]=max(mx[u],siz[v]);
	}
	mx[u]=max(mx[u],sz-siz[u]);
	if(mx[u]<mx[rt]) rt=u;
}
void dfs(int u,int fa,int mi,int mx)
{
	mi=min(mi,u);mx=max(mx,u);
	s[rt].pb({mi,mx,a[u],0});
	f[u].pb({mi,mx,rt,0});
	for(int v:g[u]) if(v^fa && !vis[v])
		dfs(v,u,mi,mx);
}
void solve(int u)
{
	vis[u]=1;dfs(u,0,inf,0);
	for(int v:g[u]) if(!vis[v])
	{
		sz=siz[v];rt=0;
		find(v,u);solve(rt);
	}
}
void add(int x,int c)
{
	for(int i=x;i<=n;i+=i&(-i)) b[i]+=c;
}
int ask(int x)
{
	int r=0;
	for(int i=x;i>0;i-=i&(-i)) r+=b[i];
	return r;
}
void clear(int x)
{
	for(int i=x;i<=n;i+=i&(-i)) b[i]=0;
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		g[u].pb(v);g[v].pb(u);
	}
	mx[0]=sz=n;find(1,0);solve(rt);
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read(),x=read();
		for(node t:f[x]) if(l<=t.x && t.y<=r)
			{s[t.z].pb({l,r,i,1});break;}
	}
	memset(c,0x3f,sizeof c);
	for(int i=1;i<=n;i++)
	{
		sort(s[i].begin(),s[i].end(),[&](node a,node b)
		{return a.x>b.x || (a.x==b.x && a.tp<b.tp);});
		for(node t:s[i])
		{
			if(t.tp) ans[t.z]=ask(t.y);
			else if(t.y<c[t.z])
			{
				if(c[t.z]<inf) add(c[t.z],-1);
				add(c[t.z]=t.y,1);
			}
		}
		for(node t:s[i]) if(!t.tp)
			c[t.z]=inf,clear(t.y);
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
}
posted @ 2022-07-08 16:43  C202044zxy  阅读(252)  评论(0编辑  收藏  举报