hihocoder1994 树与落叶 DFS+前缀和+二分

DFS找到节点删除的时间,删除的时间其实就是子树的最长链,然后给每个点打一个时间戳,然后求每个时间点对应删除的节点的个数,对于1-max_time时间戳求一个前缀和,然后二分找到和m距离最近的那一天

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<map>
using namespace std;
const int maxx = 2e5+6;
const int INF = 0x3f3f3f3f;
vector<int>v;
int ver[maxx],edge[maxx],Next[maxx],head[maxx];
int sz[maxx];
int vis[maxx];
int cnt[maxx];
int pre[maxx];
int tot,n,mx,m;
void add(int u,int v){
  ver[++tot]=v;Next[tot]=head[u];head[u]=tot;
  ver[++tot]=u;Next[tot]=head[v];head[v]=tot;
}
int dfs(int u,int fa){
   for (int i=head[u];i;i=Next[i]){
      int v=ver[i];
      if (v==fa)continue;
      vis[u]=max(vis[u],dfs(v,u));
   }
   return vis[u]+1;
}
int main(){
  int q;
  int uu,vv,st;
  scanf("%d%d",&n,&q);
  tot=0;
  for (int i=1;i<=n;i++){
    scanf("%d",&uu);
    sz[uu]++;
    sz[i]++;
    if(uu==0){
      st=i;
      continue;
    }
    add(uu,i);
  }
  for (int i=1;i<=n;i++){
    if(sz[i]==1 && i!=st){
        vis[i]=1;
    }
  }
  dfs(st,0);
  mx=0;
  for (int i=1;i<=n;i++){
     cnt[vis[i]]++;
     mx=max(mx,vis[i]);
  }
  for (int i=1;i<=mx;i++){
    pre[i]=pre[i-1]+cnt[i];
  }
  pre[0]=0;
  v.push_back(n);
  for (int i=1;i<=mx;i++){
    v.push_back(n-pre[i]);
  }
  reverse(v.begin(),v.end());
  int ans;
  while(q--){
   scanf("%d",&m);
   int pos=lower_bound(v.begin(),v.end(),m)-v.begin();
  // cout<<"ss"<<pos<<endl;
   if (pos==v.size()){
      printf("1
");
   }else if (pos==0){
      printf("%d
",v.size());
   }else{
      if (abs(v[pos]-m)<=abs(v[pos-1]-m)){
         printf("%d
",v.size()-pos);
      }else {
         printf("%d
",v.size()-pos+1);
      }
   }
  }
  return 0;
}
原文地址:https://www.cnblogs.com/bluefly-hrbust/p/11601260.html