[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;
}
posted @ 2025-01-20 18:44  JoeyJiang  阅读(9)  评论(0)    收藏  举报