原題面: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;
}