牛客多校4 A Ancient Distance 线段树+dfs序

思路源于某巨佬:巨佬博客

对于 1<=k<=n 答案最大是n-1 最小是0   于是我们从大到小枚举答案(这样的话小的值会覆盖大的值)   然后看这个答案对应的最小k是多少  最后在做一遍前缀最小值就行了 

找ans对应最小k的过程肯定是贪心的  我们找到深度最大的点 向上走ans步(找ans级祖先) 然后把它的子树都标记就可以了 

当最大的深度<=nowans时 退出循环

当ans 确定的时候  最多进行 n/(ans+1) 次操作 每次操作复杂度是 logn级别的

所以 总复杂度 是 nlogn^2  (调和级数求和是logn级别的)

#include
using namespace std;
const int N = 2e5+100;
vectorg[N],st;
int tot,in[N],out[N],n,ans[N],rnk[N];
int fa[N][22],dep[N];
typedef long long ll;
void init(){
	tot=0;
	for(int i = 1; i <= n; i++) g[i].clear(),ans[i]=n-1;
}
void dfs(int u,int f){
	rnk[in[u]=++tot]=u;
	dep[u]=dep[f]+1;
	fa[u][0]=f;
	for(int i = 1; i <= 19; i++) fa[u][i]=fa[fa[u][i-1]][i-1];
	for(auto v:g[u]){
		if(v==f) continue;
		dfs(v,u);
	}
	out[u]=tot;
}
int kth(int u,int k){
	int bit=0;
	while(k){
		if(k&1) u=fa[u][bit];
		k>>=1;
		bit++;
	}
	return u;
}
struct segTree{
	int nd,mx;
	bool cov;
}T[N<<2];
#define ls id<<1
#define rs ls|1
#define mid (l+r>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
void pushup(int id){
	T[id].mx=0;
	if(!T[ls].cov&&T[ls].mx>T[id].mx) T[id]=T[ls];
	if(!T[rs].cov&&T[rs].mx>T[id].mx) T[id]=T[rs]; 
}
void update(int id,int l,int r,int L,int R,int v){
	if(L<=l&&R>=r){
		T[id].cov=v;
		return;
	} 
	if(L<=mid) update(lson,L,R,v);
	if(R>mid) update(rson,L,R,v);
	pushup(id);
}
void build(int id,int l,int r){
	T[id].cov=false;
	if(l==r){
		T[id].mx=dep[T[id].nd=rnk[l]];
		return;
	}
	build(lson);build(rson);
	pushup(id);
}
int main(){
	while(~scanf("%d",&n)){
		init();
		for(int i = 2; i <= n; i++){
			int x;
			scanf("%d",&x);
			g[i].push_back(x);
			g[x].push_back(i);
		}
		dep[0]=-1;dfs(1,0);
		build(1,1,n);
		for(int nowans=n-1; nowans >= 0; nowans--){
			int ndcos=0;
			st.clear();
			while(1){
				ndcos++;
				if(T[1].mx<=nowans) break;
				int u = T[1].nd;
				u = kth(u,nowans);
				update(1,1,n,in[u],out[u],1);
				st.push_back(u);
			}	
			ans[ndcos]=nowans;
			for(auto v:st) update(1,1,n,in[v],out[v],0);
		}
		for(int i = 2; i <= n; i++) ans[i]=min(ans[i],ans[i-1]);
		ll sum=0;
		for(int i = 1; i <= n; i++) sum+=ans[i];
		printf("%lld\n",sum);
	}
	return 0;
}

 

你可能感兴趣的:(牛客多校)