P8844 [傳智杯 #4 初賽] 小卡與落葉

TanHaoren發表於2024-08-17

原題面:P8844 [傳智杯 #4 初賽] 小卡與落葉

大概題意:

給你一棵有 \(n(1\le n\le 10^5)\) 個結點的有根樹,根結點標號為 \(1\),根節點的深度為 \(1\),最開始整棵樹的所有結點都是綠色的。

小卡有 \(m(1\le m \le 10^5)\) 個操作。

操作一:把整棵樹都染綠,之後讓深度 \(\ge x\) 的結點變黃。

操作二:詢問一個結點 \(x\) 的子樹中有多少個黃色結點。

首先可以想到暴力做法。每次詢問都暴力查詢深度 \(\ge x\) 的結點的個數,時間為 \(O(n^2)\)

接下來考慮怎麼最佳化。如何詢問整個子樹內的節點狀態?可以想到 dfs序。在一個子樹內,dfs序是連續的。一個子樹 \(x\)\(dfn\) 範圍是 \(dfn_x\) --> \(dfn_x+siz_x-1\)

於是問題就轉化成,在查詢某個 \(dfn\) 範圍內,所有深度大於 \(x\) 的節點數。可以想到這是一個二維偏序問題。可以將每個點的 \(dfn\) 當作橫座標,\(deep\) 當作縱座標,將所有詢問離線下來後用樹狀陣列處理。

時間為 \(O(nlogn)\)

Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int f=1,x=0;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return f*x;
}
const int N=1e5+5;
int n,m;
vector<int>v[N];
int cnt,dfn[N],siz[N];
int dep[N],maxd;
void dfs(int x,int fa){
    dfn[x]=++cnt;
    siz[x]++;
    for(auto i:v[x]){
        if(i==fa)continue;
        dep[i]=dep[x]+1;
        maxd=max(maxd,dep[i]);
        dfs(i,x);
        siz[x]+=siz[i];
    }
}
int ans[N];
int cnt1,cnt2;
struct node{
    int x,y;
    int id,ff;
    bool operator<(const node& p)const{
        if(x==p.x)return y<p.y;
        else return x<p.x;
    }
}a[N],b[N];
int c[N];
inline int lowbit(int x){
    return x&-x;
}
inline void upd(int x,int w){
    for(int i=x;i<=maxd;i+=lowbit(i)){
        c[i]+=w;
    }
}
inline int qur(int x){
    int res=0;
    for(int i=x;i;i-=lowbit(i)){
        res+=c[i];
    }
    return res;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n-1;i++){
        int x=read(),y=read();
        v[x].emplace_back(y);
        v[y].emplace_back(x);
    }
    dep[1]=1;
    dfs(1,0);
    for(int i=1;i<=n;i++){
        a[i].x=dfn[i],a[i].y=dep[i];
    }
    sort(a+1,a+1+n);
    int now=maxd+1;
    for(int i=1;i<=m;i++){
        int op=read();
        if(op==1)now=read();
        else{
            int x=read();
            cnt2++;
            if(now>maxd){
                ans[cnt2]=0;
                continue;
            }
            else if(now==1){
                ans[cnt2]=siz[x];
                continue;
            }
            int xa=dfn[x],xb=dfn[x]+siz[x]-1,ya=now,yb=maxd;
            b[++cnt1]={xa-1,ya-1,cnt2,1};
            b[++cnt1]={xa-1,yb,cnt2,-1};
            b[++cnt1]={xb,ya-1,cnt2,-1};
            b[++cnt1]={xb,yb,cnt2,1};
        }
    }
    sort(b+1,b+1+cnt1);
    now=0;
    for(int i=1;i<=cnt1;i++){
        int x=b[i].x,y=b[i].y;
        while(now+1<=n&&a[now+1].x<=x){
            now++;
            upd(a[now].y,1);
        }
        ans[b[i].id]+=qur(y)*b[i].ff;
    }
    for(int i=1;i<=cnt2;i++)printf("%d\n",ans[i]);
    return 0;
}

相關文章