【Codeforces】CF1039D You Are Given a Tree

题目链接

CF方向
Luogu方向

题目解法

如果给定 k k k,如何求最多的路径数?
考虑贪心,如果连到一个点上的最长路与次长路构成的路径长度 ≥ k \ge k k,那么就选定这一条,否则把最长链的长度传给父亲
时间复杂度 O ( n ) O(n) O(n)

考虑根号分治
注意到对于 k > n k>\sqrt n k>n 的情况,链的个数 ≤ n \le \sqrt n n ,同时 k k k 增大时,答案是不增的,所以考虑对于每一个答案,二分当前答案的范围,时间复杂度 O ( n n l o g n ) O(n\sqrt nlogn) O(nn logn)
对于 k < n k<\sqrt n k<n 的情况,暴力统计,时间复杂度 O ( n n ) O(n\sqrt n) O(nn )

此题卡常!考虑到递归树的时间复杂度较大,考虑记录 d f n dfn dfn 序及父亲,这样可以把树放在序列上做了
时间复杂度 O ( n n l o g n ) O(n\sqrt nlogn) O(nn logn)

#include <bits/stdc++.h>
using namespace std;
const int N(100100);
int n,B,mx1[N],mx2[N];
int cnt,dfn[N],fa[N];
int e[N<<1],ne[N<<1],h[N],idx;
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void add(int x,int y){ e[idx]=y,ne[idx]=h[x],h[x]=idx++;}
int calc(int k){
	int res=0;
	for(int i=1;i<=n;i++) mx1[i]=mx2[i]=0;
	for(int i=n;i>=1;i--){
		int t=dfn[i];
		if(mx1[t]+mx2[t]+1>=k) res++;
		else if(fa[t]){
			if(mx1[t]+1>mx1[fa[t]]) mx2[fa[t]]=mx1[fa[t]],mx1[fa[t]]=mx1[t]+1;
			else mx2[fa[t]]=max(mx2[fa[t]],mx1[t]+1);
		}
	}
	return res;
}
void solve1(){//k<=sqrt(n) 
	for(int i=1;i<=B;i++) printf("%d\n",calc(i));
}
void solve2(){//k>sqrt(n)
	int L=B+1; 
	for(int i=B;i>=0;i--){
		int l=L-1,r=n+1;
		while(l<r-1){
			int mid=(l+r)>>1;
			calc(mid)>=i?l=mid:r=mid;
		}
		for(int j=L;j<=l;j++) printf("%d\n",i);
		L=l+1;
	}
}
void dfs(int u){
	dfn[++cnt]=u;
	for(int i=h[u];~i;i=ne[i]) if(e[i]!=fa[u]) fa[e[i]]=u,dfs(e[i]);
}
int main(){
	n=read();B=sqrt(n);
	memset(h,-1,sizeof(h));
	for(int i=1,x,y;i<n;i++){
		x=read(),y=read();
		add(x,y),add(y,x);
	}
	dfs(1);
	solve1(),solve2();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值