题目链接
题目解法
如果给定
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(nnlogn)
对于
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(nnlogn)
#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;
}