把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5311】[Ynoi2011] 成都七中(点分树+树状数组)

点此看题面

大致题意: 有一棵树,每个点有一个颜色。每次询问在保留编号为\([l,r]\)的点时,\(x\)所在连通块的颜色数。

前言

??????

感觉今天状态又回来了?

自从写点分树以来,似乎还是第一次调完样例就能够一遍过掉这种大码量题(好像也不是很大)。

本来打算用这道题打发掉一个上午的。。。

转化:点分树

首先,直接处理连通块显然不太好搞,因此我们要把问题给转化到点分树上去。

考虑对于原树上的一个连通块,我们一定能在点分树上找到一个属于该连通块的点,使得整个连通块都在这个点的子树中。

为什么呢?因为点分树上每个子树是一个连通块,若两点不在同一子树中且没有共同祖先(共同祖先可能可以起到连接作用),就一定不连通。

于是,对于一次询问,我们可以找到\(x\)在点分树上深度最小的一个祖先,满足\(x\)和该祖先连通(即\(x\)到该祖先路径上所有点满足在\([l,r]\)范围内),就可以把询问对象转化为这个祖先。

这样一来,就变成了询问在点分树上某一点的子树内,到根节点路径上所有点都在\([l,r]\)范围内的点的颜色数

离线乱搞

前面这部分神奇的转化都是看题解的,而之后的做法由于比较套路,我是自己瞎推出来的。

考虑对于子树中的每个点,我们记它到根路径上的最小编号为\(l\),最大编号为\(r\)

显然,一个点能有贡献,必须要满足询问的左边界小于等于\(l\),右边界大于等于\(r\)

根据曾做过的一道有点类似的题目的套路,考虑从小到大枚举\(r\),然后用树状数组维护每个左边界的答案。

由于询问的是颜色数,如果贪心地去考虑,很明显,对于每种颜色我们只需维护最大的\(l\)计算答案。

这样就把难以统计的颜色数转化为数值和了。

然后好像也没什么细节了,具体实现可以详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LN 20
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define pb push_back
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,m,a[N+5],ee,lnk[N+5],ans[N+5];struct edge {int to,nxt;}e[N<<1];
struct data
{
	int op,p,l,r;I data(CI f=0,CI i=0,CI a=0,CI b=0):op(f),p(i),l(a),r(b){}
	I bool operator < (Con data& o) Con {return r^o.r?r<o.r:op>o.op;}
};vector<data> K[N+5];vector<data>::iterator it;
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		#define tn (x<<3)+(x<<1)
		#define D isdigit(c=tc())
		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
	public:
		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
		Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
		Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
		#undef D
}F;
class TreeArray//树状数组
{
	private:
		int a[N+5];
	public:
		I void U(RI x,CI v) {W(x) a[x]+=v,x-=x&-x;}//单点修改
		I int Q(RI x,RI t=0) {W(x<=n) t+=a[x],x+=x&-x;return t;}//求后缀和
}A;
class DotSolveTree//点分树
{
	private:
		int Rt,T,Sz[N+5],Mx[N+5],used[N+5],cnt[N+5],lst[N+5];
		struct Info {int id,l,r;I Info(CI p=0,CI a=0,CI b=0):id(p),l(a),r(b){}}S[N+5],f[N+5][LN+5];
		I void GetRt(CI x,int s,CI lst=0)
		{
			Sz[x]=1,Mx[x]=0;for(RI i=lnk[x];i;i=e[i].nxt) !used[e[i].to]&&
				e[i].to^lst&&(GetRt(e[i].to,s,x),Sz[x]+=Sz[e[i].to],Gmax(Mx[x],Sz[e[i].to]));
			Gmax(Mx[x],s-Sz[x]),Mx[x]<Mx[Rt]&&(Rt=x);
		}
		I void dfs(CI x,CI lst,RI l,RI r)//遍历子树
		{
			Gmin(l,x),Gmax(r,x),S[++T]=Info(x,l,r);//开栈存储信息
			for(RI i=lnk[x];i;i=e[i].nxt) !used[e[i].to]&&e[i].to^lst&&(dfs(e[i].to,x,l,r),0);
		}
		I void Solve(RI x)
		{
			used[x]=1,K[x].pb(data(1,x,x,x));
			for(RI i=lnk[x],y;i;i=e[i].nxt) if(!used[e[i].to])
			{
				dfs(e[i].to,x,x,x);W(T) y=S[T].id,
					f[y][++cnt[y]]=Info(x,S[T].l,S[T].r),K[x].pb(data(1,y,S[T].l,S[T].r)),--T;//f记录祖先信息,K中op=1表示子树内的点
				Rt=0,GetRt(e[i].to,Sz[e[i].to]),Solve(Rt);//继续处理子树
			}
		}
	public:
		I void Build() {Mx[Rt=0]=1e9,GetRt(1,n),Solve(Rt);}
		I int Find(CI x,CI l,CI r)//找到一个祖先转化询问
		{
			RI i,t=x;for(i=cnt[x];i;--i) f[x][i].l>=l&&f[x][i].r<=r&&(t=f[x][i].id);return t;
		}
		I void Calc(CI x)//求解以x为对象的询问
		{
			for(sort(K[x].begin(),K[x].end()),it=K[x].begin();it!=K[x].end();++it)
				it->op?lst[a[it->p]]<it->l&&(A.U(lst[a[it->p]],-1),A.U(lst[a[it->p]]=it->l,1),0)//对于每种颜色维护最大l
				:ans[it->p]=A.Q(it->l);//树状数组查询
			for(it=K[x].begin();it!=K[x].end();++it) it->op&&(A.U(lst[a[it->p]],-1),lst[a[it->p]]=0);//清空
		}
}D;
int main()
{
	RI i,x,y,z;for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(a[i]);
	for(i=1;i^n;++i) F.read(x),F.read(y),add(x,y),add(y,x);D.Build();//建树
	for(i=1;i<=m;++i) F.read(x),F.read(y),F.read(z),z>=x&&z<=y&&(K[D.Find(z,x,y)].pb(data(0,i,x,y)),0);//K中op=0表示询问
	for(i=1;i<=n;++i) D.Calc(i);for(i=1;i<=m;++i) F.writeln(ans[i]);return F.clear(),0;//输出答案
}
posted @ 2020-05-12 08:29  TheLostWeak  阅读(187)  评论(0编辑  收藏  举报