「C.E.L.U-03」重构 题解
看着没有人用 wqs 二分做,就写一篇题解吧。
首先,通过 Prufer 序列,我们知道,对于任意一个长度为
也就是说题目转化成了:
给定长为
直接 dp 肯定是不行的,复杂度不能接受。
观察到有
:::info[wqs 二分的实现]{open}
二分每个度数的惩罚值
:::info[凸性的感性证明]{open}
因为答案是关于每个
:::success[代码]
#include<bits/stdc++.h>
#define int __int128
using namespace std;
typedef long long ll;
int n,a[300010];
int f[300010],cnt[300010];
int w(int as,int x,int at){
return (as*x-at)*x;
}
void re(int &x){
if(x<=1) x=1;
if(x>=n-1) x=n-1;
}
int read(){
ll x;
cin>>x;
return x;
}
void pr(int x){
ll y=x;
cout<<y<<" ";
}
int div(int x,int y){
int p=(x%y+y)%y;
x-=p;
return x/y;
}
void ret(int at){
memset(f,0,sizeof(f));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++){
int u=div(at,2*a[i]);
int v=u+1;
re(u);
re(v);
int uc=w(a[i],u,at);
int vc=w(a[i],v,at);
if(vc<=uc){
f[i]=f[i-1]+vc;
cnt[i]=cnt[i-1]+v;
}
else{
f[i]=f[i-1]+uc;
cnt[i]=cnt[i-1]+u;
}
}
}
signed main(){
ios::sync_with_stdio(0);
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
int le=-1e9,ri=1e9,mid;
while(le<ri){
mid=(le+ri)>>1;
ret(mid);
if(cnt[n]>=2*n-2) ri=mid;
else le=mid+1;
}
ret(le);
pr(f[n]+(2*n-2)*le);
}
:::