题解 CF1179D 【Fedor Runs for President】
CF1179D Fedor Runs for President解题报告
更好的阅读体验
题意
- 定义简单路径为任意一个点仅经过一次的路径。
- 给定一颗
n 个点的树,求添加一条边后最多有多少无向简单路径。 - 数据范围:
2\leqslant n\leqslant 5\cdot 10^5
分析
树上斜率优化好题。
先讲一下定义:
因为树上每两个点有且仅有一条路径,所以原本树上的路径数量为
假如我们将要添加
变一下型:
那么我们的目的找到一条路径
我们运用点分治的思想,考虑枚举每一个点
设
解释一下,
这个转移方程很显然,也很容易在
size[x]=1;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last)
continue;
dfs(y,x);
size[x]+=size[y];
}
f[x]=size[x]*size[x];
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last)
continue;
f[x]=min(f[x],f[y]+(size[x]-size[y])*(size[x]-size[y]));
p[++ps]=y;
}
然后考虑
我们枚举两个儿子:
同样解释一下,
枚举它的复杂度就是
考虑树上斜率优化,我们枚举
套路性地枚举两个决策点
拆开平方,消掉相同的项就可以得到
套路性变形:
因为
注意如果
inline int x(int p){
return size[p];
}
inline int y(int p){
return f[p]+size[p]*size[p];
}
inline double slope(int a,int b){
if(x(a)==x(b))
return y(a)>y(b)? inf:-inf;
return 1.0*(y(a)-y(b))/(x(a)-x(b));
}
我们在斜率优化之前给所有儿子按照
我们分析一下时间复杂度:对于每个点,复杂度的瓶颈就是遍历到后面的所有儿子和给它所有儿子
先证明一个引理:
不妨设
对于排序,它的复杂度都是
然后,我们处理递归:
可以按照深度归纳,我们证明对于
首先对于叶子结点一定成立,因为处理它的复杂度就是
对于非叶子结点
再加上排序的复杂度,便可以得出处理
我们从
即总复杂度为
代码
#include<stdio.h>
#include<algorithm>
#define int long long
#define inf 1000000000000000000
using namespace std;
const int maxn=500005,maxm=1000005;
int i,j,k,m,n,e,ans=inf;
int start[maxn],to[maxm],then[maxm],f[maxn],size[maxn],p[maxn],q[maxn];
inline void add(int x,int y){
then[++e]=start[x],start[x]=e,to[e]=y;
}
inline bool cmp(int a,int b){
return size[a]<size[b];
}
inline int x(int p){
return size[p];
}
inline int y(int p){
return f[p]+size[p]*size[p];
}
inline double slope(int a,int b){
if(x(a)==x(b))
return y(a)>y(b)? inf:-inf;
return 1.0*(y(a)-y(b))/(x(a)-x(b));
}
void dfs(int x,int last){
int ps=0,l=1,r=0;
size[x]=1;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last)
continue;
dfs(y,x);
size[x]+=size[y];
}
f[x]=size[x]*size[x];
if(size[x]==1)
return ;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last)
continue;
f[x]=min(f[x],f[y]+(size[x]-size[y])*(size[x]-size[y]));
p[++ps]=y;
}
ans=min(ans,f[x]+(n-size[x])*(n-size[x]));
sort(p+1,p+1+ps,cmp);
q[++r]=p[1];
for(int i=2;i<=ps;i++){
while(l<r&&slope(q[l+1],q[l])<=2*(n-size[p[i]]))
l++;
ans=min(ans,f[p[i]]+f[q[l]]+(n-size[p[i]]-size[q[l]])*(n-size[p[i]]-size[q[l]]));
while(l<r&&slope(p[i],q[r-1])<=slope(q[r],q[r-1]))
r--;
q[++r]=p[i];
}
}
signed main(){
scanf("%lld",&n);
for(i=1;i<n;i++){
int x,y;
scanf("%lld%lld",&x,&y);
add(x,y),add(y,x);
}
dfs(1,0);
printf("%lld\n",n*(n-1)/2+(n*n-ans)/2);
return 0;
}