[关闭]
@xiaoziyao 2020-11-30T08:43:03.000000Z 字数 4161 阅读 963

CF1179D Fedor Runs for President解题报告

解题报告


CF1179D Fedor Runs for President解题报告

更好的阅读体验

题意

分析

树上斜率优化好题。

先讲一下定义:的子树大小,的儿子数量,所有儿子形成的集合,路径上所有点形成的集合。

因为树上每两个点有且仅有一条路径,所以原本树上的路径数量为,然后考虑添加一条边会增加多少条无向简单路径:

假如我们将要添加,那么我们先提出的路径,假如在路径上,我们称为以为根,不向路径上扩展的子树大小。那么我们可以知道每一颗这样的子树之间都多了一条路径,根据乘法原理有这条边对答案贡献为(除是为了去重)。

变一下型:

那么我们的目的找到一条路径,让最小。

我们运用点分治的思想,考虑枚举每一个点,计算子树中,每一条经过的路径的答案。

为从子树中任意结点,且满足让上式最小的路径,那么很容易列出转移方程:

解释一下,相当于(因为的儿子在路径上)。

这个转移方程很显然,也很容易在的复杂度中求出。

  1. size[x]=1;
  2. for(int i=start[x];i;i=then[i]){
  3. int y=to[i];
  4. if(y==last)
  5. continue;
  6. dfs(y,x);
  7. size[x]+=size[y];
  8. }
  9. f[x]=size[x]*size[x];
  10. for(int i=start[x];i;i=then[i]){
  11. int y=to[i];
  12. if(y==last)
  13. continue;
  14. f[x]=min(f[x],f[y]+(size[x]-size[y])*(size[x]-size[y]));
  15. p[++ps]=y;
  16. }

然后考虑子树中,每一条经过的路径,设这条路径经过两个的儿子,那么我们可以把代表的路径,代表的路径和拼起来计算答案。

我们枚举两个儿子:

同样解释一下,是除了子树和子树中的结点外所有的结点,因为在均路径上,所以其他的点都必须分配到子树中,即

枚举它的复杂度就是了,加上,复杂度肯定无法通过本题。

考虑树上斜率优化,我们枚举的儿子,然后在斜率优化中求出决策点

套路性地枚举两个决策点,且更优,即

拆开平方,消掉相同的项就可以得到

套路性变形:

因为,那么除过来不变号,化成斜率式:

注意如果需要特判一下:

  1. inline int x(int p){
  2. return size[p];
  3. }
  4. inline int y(int p){
  5. return f[p]+size[p]*size[p];
  6. }
  7. inline double slope(int a,int b){
  8. if(x(a)==x(b))
  9. return y(a)>y(b)? inf:-inf;
  10. return 1.0*(y(a)-y(b))/(x(a)-x(b));
  11. }

我们在斜率优化之前给所有儿子按照排一下序,就可以上斜率优化板子了。


我们分析一下时间复杂度:对于每个点,复杂度的瓶颈就是遍历到后面的所有儿子和给它所有儿子一遍。

先证明一个引理:

不妨设,那么

对于排序,它的复杂度都是的,因此我们可以直接看做每个点都需要的处理。

然后,我们处理递归:

可以按照深度归纳,我们证明对于,处理它的复杂度一定是的。

首先对于叶子结点一定成立,因为处理它的复杂度就是的。

对于非叶子结点,假如它的儿子是,且它每个儿子都满足这个复杂度约束,那么处理它儿子的总复杂度为,按照上面的引理一直处理下去,就可以得到上式小于等于,因为,所以递归所有儿子的复杂度是的。

再加上排序的复杂度,便可以得出处理的复杂度是的。

我们从开始,那么由上面的证明可以知道处理的时间复杂度为

即总复杂度为

代码

  1. #include<stdio.h>
  2. #include<algorithm>
  3. #define int long long
  4. #define inf 1000000000000000000
  5. using namespace std;
  6. const int maxn=500005,maxm=1000005;
  7. int i,j,k,m,n,e,ans=inf;
  8. int start[maxn],to[maxm],then[maxm],f[maxn],size[maxn],p[maxn],q[maxn];
  9. inline void add(int x,int y){
  10. then[++e]=start[x],start[x]=e,to[e]=y;
  11. }
  12. inline bool cmp(int a,int b){
  13. return size[a]<size[b];
  14. }
  15. inline int x(int p){
  16. return size[p];
  17. }
  18. inline int y(int p){
  19. return f[p]+size[p]*size[p];
  20. }
  21. inline double slope(int a,int b){
  22. if(x(a)==x(b))
  23. return y(a)>y(b)? inf:-inf;
  24. return 1.0*(y(a)-y(b))/(x(a)-x(b));
  25. }
  26. void dfs(int x,int last){
  27. int ps=0,l=1,r=0;
  28. size[x]=1;
  29. for(int i=start[x];i;i=then[i]){
  30. int y=to[i];
  31. if(y==last)
  32. continue;
  33. dfs(y,x);
  34. size[x]+=size[y];
  35. }
  36. f[x]=size[x]*size[x];
  37. if(size[x]==1)
  38. return ;
  39. for(int i=start[x];i;i=then[i]){
  40. int y=to[i];
  41. if(y==last)
  42. continue;
  43. f[x]=min(f[x],f[y]+(size[x]-size[y])*(size[x]-size[y]));
  44. p[++ps]=y;
  45. }
  46. ans=min(ans,f[x]+(n-size[x])*(n-size[x]));
  47. sort(p+1,p+1+ps,cmp);
  48. q[++r]=p[1];
  49. for(int i=2;i<=ps;i++){
  50. while(l<r&&slope(q[l+1],q[l])<=2*(n-size[p[i]]))
  51. l++;
  52. ans=min(ans,f[p[i]]+f[q[l]]+(n-size[p[i]]-size[q[l]])*(n-size[p[i]]-size[q[l]]));
  53. while(l<r&&slope(p[i],q[r-1])<=slope(q[r],q[r-1]))
  54. r--;
  55. q[++r]=p[i];
  56. }
  57. }
  58. signed main(){
  59. scanf("%lld",&n);
  60. for(i=1;i<n;i++){
  61. int x,y;
  62. scanf("%lld%lld",&x,&y);
  63. add(x,y),add(y,x);
  64. }
  65. dfs(1,0);
  66. printf("%lld\n",n*(n-1)/2+(n*n-ans)/2);
  67. return 0;
  68. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注