[Ynoi2011] 成都七中 题解

题面
看到处理连通块问题想到点分治,条件可以转化为若 \(u\)\(x\) 在同一个连通块中,则 \(u\)\(x\) 的路径上所有点都在 \([l,r]\) 内。我们可以每层分治都处理出 \(x\) 到分治中心的路径,再回答询问。每一个点一定会被一个分治中心访问到,且它被访问的第一次就是最优的情况。

递归得到当前 \(l,r\) 表示中心到 \(x\) 的路径上编号最小的点是 \(l\),最大的点是 \(r\),存进 vector 里面。我们再把当前能处理的询问都存进另一个 vector 里面,然后做一个双指针,把询问和路径都按 \(r\) 排序,因为要求路径的 \(l>=L_q\)\(r<=R_q\),所以我们对每一种颜色都在 bit 对应的 \(l\) 上面加上 \(1\),如果这个颜色已经出现过,我们在最大的 \(l\) 上加这个 \(1\),因为这样最优。询问树状数组的 \([l-1,r]\) 区间就可以得到颜色数量了。

说实话感觉没那么难,但是就是有点难以想到把路径约束转化为 \(\min,\max\) 的限制。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = 1e5+10;
vector<int>g[N];
int c[N],maxp[N],siz[N],rt,tot,vis[N],ans[N],lst[N],st[N],top;
struct qy{
    int l,r,id;
};
vector<qy>q[N],p,w;
int lowbit(int x){
    return x&(-x);
}
int t[N];
void add(int p,int v){
    for(int i=p;i<=n;i+=lowbit(i)){
        t[i]+=v;
    }
}
int ask(int p){
    int res=0;
    for(int i=p;i;i-=lowbit(i)){
        res+=t[i];
    }
    return res;
}
int query(int l,int r){
    return ask(r)-ask(l-1);
}
void getct(int u,int f){
    siz[u]=1;
    maxp[u]=0;
    for(auto v:g[u]){
        if(v==f||vis[v])continue;
        getct(v,u);
        siz[u]+=siz[v];
        maxp[u]=max(maxp[u],siz[v]);
    }
    maxp[u]=max(maxp[u],tot-siz[u]);
    if(maxp[u]<maxp[rt])rt=u;
}
bool cmp(qy a,qy b){
    return a.r<b.r;
}
void dfs(int x,int f,int l,int r){
    siz[x]=1;
    p.push_back({l,r,c[x]});
    for(auto [L,R,id]:q[x]){
        if(ans[id]||l<L||r>R)continue;
        w.push_back({L,R,id});
    }
    for(auto v:g[x]){
        if(vis[v]||v==f)continue;
        dfs(v,x,min(v,l),max(v,r));
        siz[x]+=siz[v];
    }
}
void solve(int x){
    p.clear();w.clear();
    dfs(x,0,x,x);
    sort(p.begin(),p.end(),cmp);
    sort(w.begin(),w.end(),cmp);
    int cur=0;
    for(auto [l,r,id]:w){
        while(cur<p.size()&&p[cur].r<=r){
            int c=p[cur].id;
            if(!lst[c]){
                lst[c]=p[cur].l;
                add(p[cur].l,1);
                st[++top]=p[cur].id;
            }else if(p[cur].l>lst[c]){
                add(lst[c],-1);
                lst[c]=p[cur].l;
                add(lst[c],1);
            }
            cur++;   
        }
        ans[id]=query(l,n);
    }
    while(top){
        add(lst[st[top]],-1);
        lst[st[top]]=0;
        top--;
    }
}
void ctd(int x){
    vis[x]=1;
    solve(x);
    for(auto v:g[x]){
        if(vis[v])continue;
        tot=siz[v];
        maxp[rt=0]=N;
        getct(v,0);
        ctd(rt);
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>c[i];
    }
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    for(int i=1;i<=m;i++){
        int l,r,x;
        cin>>l>>r>>x;
        q[x].push_back({l,r,i});
    }
    tot=n;
    maxp[rt]=N;
    getct(1,0);
    ctd(rt);
    for(int i=1;i<=m;i++){
        cout<<ans[i]<<endl;
    }
    return 0;
}
posted @ 2026-08-10 21:45  farenara_sayowell  阅读(4)  评论(0)    收藏  举报