CF1039D You Are Given a Tree 根号分治+二分
如果 $k$ 值确定的话,我们直接来一个 $O(n)$ 的贪心就行.
那么我们就将 $k$ 分为大于 $B$ 和小于 $B$ 两部分处理.
对于小于 $B$ 的部分,暴力处理,复杂度为 $O(nB)$.
对于大于 $B$ 的部分,取值分别为 $[0,\frac{n}{B}]$ 且依次递减.
那么我们就可以二分每种取值的区间.
由于有 $\frac{n}{B}$ 种取值,计算每个取值区间为 $O(n \log n)$,故这部分复杂度为 $\frac{n}{B} n \log n$
一般来说,这个 $B$ 取 200 左右是比较优的.
code:
#include <bits/stdc++.h> #define B 203 #define N 100006 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,edges,tim; int hd[N],to[N<<1],nex[N<<1],f[N],vis[N],maxx[N],po[N],fa[N]; void add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } void dfs(int x,int ff) { po[++tim]=x,fa[x]=ff; for(int i=hd[x];i;i=nex[i]) if(to[i]!=ff) dfs(to[i],x); } int solve(int x) { f[x]=0; int i,j; memset(vis,0,sizeof(vis)); for(i=1;i<=n;++i) maxx[i]=1; for(i=tim;i>=1;--i) { int u=po[i]; if(fa[u]&&!vis[u]) { if(vis[fa[u]]==0) { if(maxx[fa[u]]+maxx[u]>=x) ++f[x],maxx[fa[u]]=0,vis[fa[u]]=1; else maxx[fa[u]]=max(maxx[fa[u]],maxx[u]+1); } } } return f[x]; } void init(int M) { int i,j; f[1]=n; for(i=2;i<=M;++i) solve(i); } int main() { // setIO("input"); int i,j; scanf("%d",&n); for(i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); add(x,y),add(y,x); } dfs(1,0); init(min(n,B)); if(n<=B) { for(i=1;i<=n;++i) printf("%d\n",f[i]); } else { int An; for(i=B+1;i<=n;i=An+1) { An=i; int l=i,r=n; int tmp=solve(l); while(l<=r) { int mid=(l+r)>>1; if(solve(mid)==tmp) An=mid,l=mid+1; else r=mid-1; } for(j=i;j<=An;++j) f[j]=tmp; } for(i=1;i<=n;++i) printf("%d\n",f[i]); } return 0; }