[后序遍历][堆][倍增] Jzoj P4811
题解
- 首先,我们很容易就可以发现,每个人的走法就是后序遍历的走法
- 询问删掉一个人之后又多少个人变化,也就是该点到根路径上人的数量
- 那么我们就可以用堆维护房间,然后用倍增往上跳找人数
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <vector> 5 #include <cmath> 6 #include <algorithm> 7 using namespace std; 8 const int N=100010; 9 int n,t,tot,cnt,ans,dfx[N],d[N],p[N],heap[N*4],deep[N],f[N][20]; 10 bool bz[N]; 11 vector<int> a[N]; 12 bool cmp(int x,int y) { return dfx[x]<dfx[y]; } 13 void dfs(int x,int fa) 14 { 15 f[x][0]=fa,deep[x]=deep[fa]+1; 16 for (int i=0;i<a[x].size();i++) if (a[x][i]!=fa) dfs(a[x][i],x); 17 dfx[x]=++cnt; 18 } 19 void insert(int x) 20 { 21 heap[++tot]=x; 22 for (int i=tot;i>1&&p[heap[i]]<p[heap[i/2]];i/=2) swap(heap[i],heap[i/2]); 23 } 24 int get() 25 { 26 int x=heap[1],i=1; 27 heap[1]=heap[tot],heap[tot--]=0; 28 while (1) 29 { 30 int l=i*2,r=i*2+1,m; 31 if (!(p[heap[i]]>p[heap[l]]&&l<=tot||p[heap[i]]>p[heap[r]]&&r<=tot)) break; 32 if (r>tot||p[heap[l]]<p[heap[r]]) m=l; else m=r; 33 swap(heap[i],heap[m]),i=m; 34 } 35 return x; 36 } 37 int jump(int x) 38 { 39 for (int i=log2(deep[x]);i>=0;i--) if (bz[f[x][i]]) x=f[x][i]; 40 return x; 41 } 42 int main() 43 { 44 scanf("%d%d",&n,&t); 45 for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),a[x].push_back(y),a[y].push_back(x); 46 for (int i=1;i<=n;i++) d[i]=i,sort(a[i].begin(),a[i].end()); 47 dfs(1,0); 48 for (int j=1;j<=log2(n);j++) for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; 49 sort(d+1,d+n+1,cmp); 50 for (int i=1;i<=n;i++) p[d[i]]=i; 51 for (int i=1;i<=n;i++) insert(i); 52 for (int i=1,x,y,op;i<=t;i++) 53 { 54 scanf("%d%d",&op,&x); 55 if (op==1) 56 { 57 for (;x;x--) bz[ans=get()]=1; 58 printf("%d\n",ans); 59 } 60 else printf("%d\n",deep[x]-deep[y=jump(x)]),insert(y),bz[y]=0; 61 } 62 }