[Ynoi2011] 成都七中
题目描述
给你一棵 \(n\) 个节点的树,每个节点有一种颜色,有 \(m\) 次查询操作。
查询操作给定参数 \(l\ r\ x\),需输出:
将树中编号在 \([l,r]\) 内的所有节点保留,\(x\) 所在连通块中颜色种类数。
每次查询操作独立。
对于 \(100\%\) 的数据,所有出现过的数在 \([1,10^5]\) 之间,保证每次输入的 \(l \le x \le r\)。
题解
首先建出原树的点分树,有结论:令连通块中最浅的节点为 \(p\),则连通块内所有节点均在 \(p\) 在点分树上的子树内。
证明:
考虑反证法,假设存在一个点 \(q\) 不在 \(p\) 的子树内。
众所周知,点分树上有性质:点分树上\(u\) 与 \(v\) 的最近公共祖先一定在原树 \(u\) 到 \(v\) 的路径上。
所以,如果 \(p\) 与 \(q\) 联通,那么必然会经过一个更浅且同样在连通块内的点 \(r\),矛盾。
证毕。
所以,可以在点分治的过程中,记录每个点到当前根的路径上,编号的最大最小值。具体地,令 \(rt\) 为当前的根,若询问 \((l,r,x)\) 满足 \(l\leq mn_x\leq mx_x\leq r\),则 \(rt\) 在连通块内,则最小的 \(rt\) 即为要求的 \(p\)。
为了计数不重不漏,所以只在每个询问的 \(p\) 处进行统计,于是询问变为对于子树 \(p\),查询 \(|\{c_u|u\in T_p\wedge l\leq mn_u\leq mx_u\leq r\}|\),这是一个二维数点问题,此处不做赘述。时间复杂度为 \(O(n\log^2 n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define allc(x) x.begin(),x.end()
const int N=1e5+9;
int fi[N],ne[N<<1],to[N<<1],adj;
void AdEg(int x,int y){
ne[++adj]=fi[x];
fi[x]=adj;
to[adj]=y;
}
int a[N],ans[N],ql[N],qr[N],qx[N],n,m;
vector<int> q[N];
int siz[N],vis[N],root;
void DFS1(int x,int tot,int fa,vector<int> &v){
int flag=1;
siz[x]=1,v.push_back(x);
for(int i=fi[x];i;i=ne[i]){
int y=to[i];
if(vis[y]) continue ;
if(y==fa) continue ;
DFS1(y,tot,x,v);
siz[x]+=siz[y];
if(siz[y]>(tot>>1)) flag=0;
}
if(tot-siz[x]>(tot>>1)) flag=0;
if(flag) root=x;
}
int mx[N],mn[N];
void DFS2(int x,int fa){
siz[x]=1;
mx[x]=max(mx[x],x),mn[x]=min(mn[x],x);
for(int i=fi[x];i;i=ne[i]){
int y=to[i];
if(vis[y]) continue ;
if(y==fa) continue ;
mx[y]=mx[x],mn[y]=mn[x];
DFS2(y,x);
siz[x]+=siz[y];
}
}
int tr[N],pos[N];
void Add(int x,int k){
while(x<=n){
tr[x]+=k;
x+=x&-x;
}
}
int Ask(int x){
int sum=0;
while(x){
sum+=tr[x];
x&=x-1;
}
return sum;
}
int Ask(int l,int r){return Ask(r)-Ask(l-1);}
void Solve(int x,int tot){
vector<int> son;
DFS1(x,tot,-1,son);
x=root,vis[x]=1;
for(int u:son) mx[u]=0,mn[u]=n+1;
DFS2(x,-1);
vector<array<int,3>> pr;
for(int u:son){
vector<int> tmp;
for(int i:q[u]){
if(ql[i]<=mn[u]&&mx[u]<=qr[i]) pr.push_back({qr[i],i,1});
else tmp.push_back(i);
}
q[u]=tmp;
}
for(int u:son) pr.push_back({mx[u],u,0});
sort(allc(pr),[](auto x,auto y){return x[0]^y[0]?x[0]<y[0]:x[2]<y[2];});
vector<pair<int,int>> rb;
for(auto t:pr){
if(t[2]){
ans[t[1]]=Ask(ql[t[1]],n);
}else{
if(mn[t[1]]>pos[a[t[1]]]){
if(pos[a[t[1]]]){
Add(pos[a[t[1]]],-1);
rb.push_back({pos[a[t[1]]],-1});
}
pos[a[t[1]]]=mn[t[1]];
Add(mn[t[1]],1);
rb.push_back({mn[t[1]],1});
}
}
}
for(auto t:pr) pos[a[t[1]]]=0;
for(auto p:rb) Add(p.first,-p.second);
for(int i=fi[x];i;i=ne[i]){
int y=to[i];
if(vis[y]) continue ;
Solve(y,siz[y]);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
#define endl '\n'
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,u,v;i<n;i++) cin>>u>>v,AdEg(u,v),AdEg(v,u);
for(int i=1;i<=m;i++) cin>>ql[i]>>qr[i]>>qx[i],q[qx[i]].push_back(i);
Solve(1,n);
for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
return 0;
}

浙公网安备 33010602011771号