【题目描述】
音无彩名给了你一棵n个节点的树,每个节点有一种颜色,有m次查询操作
查询操作给定参数l r x,需输出:
将树中编号在[l,r]内的所有节点保留,x所在联通块中颜色种类数
每次查询操作独立
【思路】
首先有一个性质:对于一次询问,一定可以在点分树上找到一个点,满足这个点在本次询问中和x在同一个连通块中,且x所在的整个连通块都是该点在点分树中的一个子树。证明可以考虑反证法。那么我们把询问都离线,放到点分树上对应的点,在这个位置处理这个询问。现在,我们枚举每个点,处理与它有关的询问。对于它点分树的子树上的每个点,我们可以求出它们和分治中心路径上最大的编号和最小的编号的点,于是一个点的信息可以用这样一个三元组表示: ( m n , m x , c o l ) (mn,mx,col) (mn,mx,col)。现在问题变成了一个偏序问题:一个点对一个询问有贡献当且仅当 m n > = l 且 m x < = r mn>=l且mx<=r mn>=l且mx<=r,因为满足这个条件的点就和分治中心联通,就一定和x联通。所以我们可以把所有点和询问按mn降序排序,保证第一个限制满足。然后再用树状数组以mx为下标即可实现满足第二个限制。问题在于我们不能重复统计一个颜色的贡献。对于每一种颜色,我们可以考虑在树状数组里只维护它的mx的最小值。因为对于两个均满足 m n > = l mn>=l mn>=l的同色的点,如果mx小的那个点对答案都没有贡献,那么另一个点必然没有贡献,所以这个方法是正确的。
代码:
#include<bits/stdc++.h>
#define re register
#define F(i,a,b) for(int re i=a;i<=b;++i)
#define D(i,a,b) for(int re i=a;i>=b;--i)
using namespace std;
const int N=1e5+5;
inline int red(){
int re data=0;bool w=0;char re ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return w?-data:data;
}
int n,m,a,b,col[N];
vector<int>g[N];
struct node{int mn,mx,rt,op;};
vector<node>t[N],q[N];
int siz[N],mz=1e9,rt=0;
bool vis[N];
inline void cmax(int&x,const int y){(x<y)&&(x=y);}
inline void cmin(int&x,const int y){(x>y)&&(x=y);}
void findrt(int u,int fa,const int tot){
siz[u]=1;int v;int mx=0;
D(i,g[u].size()-1,0){
v=g[u][i];
if(v!=fa&&!vis[v]){
findrt(v,u,tot);siz[u]+=siz[v];
cmax(mx,siz[v]);
}
}
cmax(mx,tot-siz[u]);
if(mx<mz)mz=mx,rt=u;
}
void dfs(int u,int mn,int mx,int fa){
cmin(mn,u);cmax(mx,u);int v;siz[u]=1;
q[rt].push_back((node){mn,mx,col[u],0});
t[u].push_back((node){mn,mx,rt,0});
D(i,g[u].size()-1,0){
v=g[u][i];
if(v!=fa&&!vis[v])
dfs(v,mn,mx,u),siz[u]+=siz[v];
}
}
void solve(int u){
vis[u]=1;dfs(u,1e9,-1e9,0);int v;
D(i,g[u].size()-1,0){
v=g[u][i];
if(!vis[v])findrt(rt=v,u,mz=siz[v]),solve(rt);
}
}
int c[N];
inline void add(int x,int v){while(x<=n)c[x]+=v,x+=x&-x;}
inline int query(int x){
int ret=0;
while(x)ret+=c[x],x^=x&-x;
return ret;
}
int tim[N],ans[N];
inline bool cmp(node a,node b){return a.mn>b.mn||(a.mn==b.mn&&a.op<b.op);}
int main()
{
n=red();m=red();
F(i,1,n)col[i]=red();
F(i,2,n){
a=red(),b=red();
g[a].push_back(b);
g[b].push_back(a);
}findrt(1,0,mz=n);solve(rt);
F(i,1,m){
int l=red(),r=red();a=red();
F(j,0,t[a].size()-1)
if(t[a][j].mn>=l&&t[a][j].mx<=r){q[t[a][j].rt].push_back((node){l,r,i,1});break;}
}memset(tim,127,sizeof(tim));
F(i,1,n){
sort(q[i].begin(),q[i].end(),cmp);
F(j,0,q[i].size()-1){
node now=q[i][j];
if(now.op)ans[now.rt]=query(now.mx);
else if(tim[now.rt]>now.mx){
if(tim[now.rt]<=1e5)add(tim[now.rt],-1);
add(tim[now.rt]=now.mx,1);
}
}F(j,0,q[i].size()-1){
node now=q[i][j];
if(now.op)continue;
tim[now.rt]=1e9;
for(int re k=now.mx;k<=n;k+=k&-k)c[k]=0;
}
}F(i,1,m)cout<<ans[i]<<"\n";
}