1. 程式人生 > >摧毀圖狀樹 - 線段樹 - 倍增

摧毀圖狀樹 - 線段樹 - 倍增

題目大意:
給你一棵無權樹,對於每個k=1…n求:
每次你可以給一個點x到其k級祖先的路徑上的所有點打上刪除標記。問最少多少次可以把所有點打上刪除標記。 n 1 0 5 n\le10^5


題解:
考慮k怎麼做,顯然先把葉子塗黑,然後每次塗黑一個點,就把其k級組先拿出來,放進堆裡。每次選擇堆中深度最大的一個點,若其未被塗黑,則塗黑,並重覆上述操作。
於是可以注意到答案是O(x+(n-x)/k)級別的,其中x是葉子個數,原因刪除所有貪心過程中刪除的點,森林的每個聯通塊打不小於k-1。
顯然對於每個k都可以做到O(答案),後半部分可以調和級數,前半部分解決辦法是隻把到葉子最小距離恰好是k的點初始化那個堆即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1) #define lint long long #define ull unsigned lint #define db long double #define pb push_back #define mp make_pair #define fir first #define sec second #define gc getchar() #define debug(x) cerr<<#x<<"="<<x #define sp <<" " #define ln <<endl
using namespace std; typedef pair<int,int> pii; typedef set<int>::iterator sit; inline int inn() { int x,ch;while((ch=gc)<'0'||ch>'9'); x=ch^'0';while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; } const int N=100010,LOG=20,INF=(INT_MAX/10-10); struct edges{ int to,pre; }e[N<<1];vector<int> v[N]; int h[N],etop,lfcnt,tlf[N],in[N],out[N],d[N],up[N][LOG],Log[N],calc[N],dfc; inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; } int dfs(int x,int fa=0) { tlf[x]=INF,up[x][0]=fa,d[x]=d[fa]+1,in[x]=++dfc; rep(i,1,Log[d[x]]) up[x][i]=up[up[x][i-1]][i-1]; for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)^fa) tlf[x]=min(tlf[x],dfs(y,x)+1); if(tlf[x]==INF) tlf[x]=0,lfcnt++;else v[tlf[x]].pb(x); return out[x]=dfc,tlf[x]; } int kthanc(int x,int k) { for(int i=0;k;k>>=1,i++) if(k&1) x=up[x][i];return x; } struct segment{ int l,r,v,ct; segment *ch[2]; }*rt; inline int push_up(segment* &rt) { return rt->v=min(rt->ch[0]->v,rt->ch[1]->v); } inline int Clr(segment* &rt) { return rt->ct=1,rt->v=INF; } inline int push_down(segment* &rt) { return Clr(rt->ch[0]),Clr(rt->ch[1]),rt->ct=0; } int build(segment* &rt,int l,int r) { rt=new segment,rt->l=l,rt->r=r,rt->v=INF,rt->ct=0; if(l==r) return 0;int mid=(l+r)>>1; return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r); } int update(segment* &rt,int p,int v) { int l=rt->l,r=rt->r,mid=(l+r)>>1; if(l==r) return rt->v=v; if(rt->ct) push_down(rt); return update(rt->ch[p>mid],p,v),push_up(rt); } int query(segment* &rt,int s,int t) { int l=rt->l,r=rt->r,mid=(l+r)>>1; if(s<=l&&r<=t) return rt->v;int ans=INF; if(rt->ct) push_down(rt); if(s<=mid) ans=min(ans,query(rt->ch[0],s,t)); if(mid<t) ans=min(ans,query(rt->ch[1],s,t)); return ans; } priority_queue<pii> q; int solve(int k) { while(!q.empty()) q.pop();Clr(rt);int ans=lfcnt; Rep(i,v[k]) q.push(mp(d[v[k][i]],v[k][i])); while(!q.empty()) { int x=q.top().sec,y;q.pop(); if(query(rt,in[x],out[x])<d[x]+k||tlf[x]<k) continue; update(rt,in[x],d[x]),ans++; if(k<d[x]) y=kthanc(x,k),q.push(mp(d[y],y)); } return ans; } int main() { int n=inn(),u,v;rep(i,2,n) Log[i]=Log[i>>1]+1; rep(i,1,n-1) u=inn(),v=inn(),add_edge(u,v),add_edge(v,u); dfs(1),build(rt,1,n),calc[1]=n;int mxd=1;rep(i,1,n) mxd=max(mxd,d[i]); for(int q=inn(),k;q;q--) if(calc[k=min(inn(),mxd)]) printf("%d\n",calc[k]); else printf("%d\n",calc[k]=solve(k)); return 0; }