正题
题目链接:https://www.luogu.com.cn/problem/CF1039D
题目大意
给出nnn个点的一棵树,然后对于k∈[1,n]k\in[1,n]k∈[1,n]求每次使用一条长度为kkk的链覆盖树并且不能重复覆盖点时最大覆盖条数。
1≤n≤1051\leq n\leq 10^51≤n≤105
解题思路
先考虑暴力怎么做,因为每条链的价值都是一,显然的一种贪心思想是能合并的就合并(没有让出一条链给另一条链腾空间的必要)。
这样的复杂度是O(n)O(n)O(n)的,但是对于每个都要求所以需要优化。
之后考虑上根号分治,对于一个kkk的答案显然不会超过nk\frac{n}{k}kn,所以可以当k≤nk\leq \sqrt nk≤n的时候暴力做,然后由于答案递增,大于n\sqrt nn的kkk答案的取值不会超过n\sqrt nn,每次二分断点即可。时间复杂度O(nnlogn)O(n\sqrt n\log n)O(nnlogn)。
其实发现这样还是不够快,可以找到一个更好的阈值,设为TTT,那么前面的复杂度就是TTT,后面的复杂度就是nTlogn\frac{n}{T}\log nTnlogn,用平衡规划的思想当T=nTlognT=\frac{n}{T}\log nT=Tnlogn时最快,也就是T=nlognT=\sqrt{n\log n}T=nlogn时最快了。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;
struct node{int to,next;
}a[N<<1];
int n,tot,cnt,dfn[N],ls[N],fa[N],f[N];
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void dfs(int x){dfn[++cnt]=x;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa[x])continue;fa[y]=x;dfs(y);}return;
}
int solve(int k){if(k==1)return n;int ans=0;for(int i=1;i<=n;i++)f[i]=1;for(int i=n;i>=1;i--){int x=dfn[i];if(f[x]&&f[fa[x]]){if(f[x]+f[fa[x]]>=k)ans++,f[fa[x]]=0;else f[fa[x]]=max(f[fa[x]],f[x]+1);}}return ans;
}
int main()
{scanf("%d",&n);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}dfs(1);int T=sqrt((double)n*(log(n)/log(2))),last,z=T+1;for(int i=1;i<=T;i++)printf("%d\n",last=solve(i));while(z<=n){int l=z+1,r=n,k=solve(z);while(l<=r){int mid=(l+r)>>1;if(solve(mid)<k)r=mid-1;else l=mid+1;}for(int i=z;i<=r;i++)printf("%d\n",k);z=r+1;}return 0;
}