T2 You Are Given a Tree (CF1039D)

· · 个人记录

T2 You Are Given a Tree (CF1039D)

CF传送门

洛谷传送门

题意简述

有一棵 n 个节点的树

在树上选择一些长度为 k 的路径,使得这些路径覆盖的点互不重复

求对于 k (1 \leq k \leq n) ,最多能选择多少条路径

数据范围
1 \leq n \leq 10^5 1 \leq u \leq v \leq n
题解

思路

首先从特殊情况考虑

覆盖问题自然可以考虑贪心 一条链,必定可以找出一个点作为“根”(即 $dep$ 最小的那个点) 对于一个节点 $o$ ,想要覆盖它 如果有一根长度为 $k$ 的链以 $o$ 为根,那自然把它数进来最好 不然就会占用 $o$ 上面的节点,显然不优 (那如果这样的链有很多条呢? 可 $o$ 只能用一次啊!选哪种都是一样的) 所以对于一个o来说,记录转移它子树最长链与次长链的长度即可 $O(n)$ 解决 *** 一个 $k$ 的情况搞定了,但 $n$ 个 $k$ 就要 $O(n^2)

7s都无济于事

所以不妨来观察一下答案

显然,ans \leq n/k

而且 ans 必然是单调不增

对任意一个 a 来说,前 ak ,答案种数不超过 a 种;后 (n-a)k ,答案种数不会超过 n/a+1(0 - n/a)

所以答案种数不会超过 a+n/a \geq 2 \times \sqrt n/a 种(当 a\sqrt n 时)

对前 \sqrt n 个来说,暴力枚举即可,反正也没多少个

对后 n-\sqrt n 个来说,就是枚举断点

有多个断点? $\Rightarrow$ 分治+二分 $O(nlogn \sqrt n)

做法

首先,dp部分

f[i] 表示包括 i节点在内的,子树下最长链长度

然后转移即可 如果找得到的话,注意 $f[i]=0$ ,因为此节点不可再用了 *** 分治+二分部分 用 $l$ , $r$ 表示查找的区间 用 $L$ , $R$表示查找的值域 如果值域确定下来了表明这是个断点,更新答案 否则dp一遍 $mid$ 的答案,再去分治 更新答案的方向应该不用管,是分治嘛,无论哪个方向都会更新到的 **注意事项** · 代码有点难以理解,慢慢想 · max值的更新方式好像这样是最正确也是最易于理解的 我原来写的不知为何错了 **AC代码** ```cpp #include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,k,sum,flag; int f[N],ans[N]; struct abc{ int to,nxt; }e[2*N]; int head[N],cnt; void add(int u,int v){ e[++cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt; } void dfs(int o,int fa){ int max1=0,max2=0; for(int i=head[o];i;i=e[i].nxt){ int v=e[i].to; if(v==fa) continue; dfs(v,o); if(f[v]>max1) max2=max1,max1=f[v]; else if(f[v]>max2) max2=f[v]; //这两行是更新max值 } if(max1+max2+1>=k) sum++,f[o]=0;//如果找得到 else f[o]=max1+1;//找不到就更新f[o] } void solve(int l,int r,int L,int R){ if(l>r || L>R) return; if(L==R){ for(int i=l;i<=r;i++) ans[i]=L; return;//记得啊 } int mid=(l+r)>>1; k=mid,sum=0; dfs(1,0); //跑一遍mid的答案 ans[mid]=sum;//别忘了更新ans[mid](因为下面不会枚举到这个点了) solve(l,mid-1,sum,R); solve(mid+1,r,L,sum); } int main(){ scanf("%d",&n); for(int i=1;i<=n-1;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } for(int i=1;i<=sqrt(n);i++){ k=i; sum=0;//sum为单次答案计数 dfs(1,0); ans[i]=sum; } solve(sqrt(n)+1,n,0,sqrt(n)); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); } ```