Loading

P4655 [CEOI2017]Building Bridges

P4655 [CEOI2017]Building Bridges

为啥黑题没紫题难啊/kk,感觉这题30天之内会掉紫

状态非常一眼&simple:设 \(dp_i\) 表示 \(1,i\) 联通的最小代价

\(s_n=\sum_{i=1}^{n}w_i\)

转移非常一眼&simple:\(dp_i=\min\{dp_j+(h_i-h_j)^2+s_{i-1}-s_j \}(j<i)\)

处理非常一眼&simple:\(dp_i=\min\{dp_j+h_j^2-s_j-2h_ih_j \}+h_i^2+s_{i-1}\)

非常一眼&simple可以想到用李超树维护,把 \(-2h_i\) 当作斜率,\(dp_j+h_j^2-s_j\) 当作 \(y\) 轴截距,全部插到李超树里,就相当于取 \(x=h_i\) 时所有直线的最小值

其实还可以斜率优化,但是那个 \(w_i\) 可能是负数不一定是凸壳,看别人要 cdq 分治,我感觉李超树简单多了,复杂度都是 \(O(n\log n)\),cdq分治写丑点还会变成 \(O(n\log^2n)\)。。。

第一次用 vim 写出黑题呢(其实这是用 vim 写的第二道题)。但是发现调试太累了,尤其是DS,就不用了qwq

没抢到最优解,只有第三,不知道为啥第一名比我快 30ms ,方法都一样,一定是我人傻常数大。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define mkp(x,y) make_pair(x,y)
#define fi first
#define se second
#define pb(x) push_back(x)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
#define N 100005
#define M 1000005
#define T (M<<2)
#define inf 1000000000000000000ll
int n,h[N],w[N],cnt,C;
LL s[N],dp[N];
int v[T];
pair<int,LL>li[N];
#define val(x,y) (1ll*li[x].fi*y+li[x].se)
#define lc (p<<1)
#define rc (p<<1|1)
void update(int id,int l=1,int r=C,int p=1){
	if(!v[p])return v[p]=id,void();
	int mid=(l+r)>>1;
	LL nol=val(id,l),nor=val(id,r),lal=val(v[p],l),lar=val(v[p],r);
	if(nol>=lal&&nor>=lar)return;
	if(nol<=lal&&nor<=lar)return v[p]=id,void();
	db inter=1.*(li[id].se-li[v[p]].se)/(li[v[p]].fi-li[id].fi);
	if(nol<=lal){
		if(inter<=mid)update(id,l,mid,lc);
		else update(v[p],mid+1,r,rc),v[p]=id;
	}else{
		if(inter>mid)update(id,mid+1,r,rc);
		else update(v[p],l,mid,lc),v[p]=id;
	}
}
LL query(int X,int l=1,int r=C,int p=1){
	LL res=inf;
	if(v[p])res=min(res,val(v[p],X));
	if(l==r)return res;
	int mid=(l+r)>>1;
	if(X<=mid)res=min(res,query(X,l,mid,lc));
	else res=min(res,query(X,mid+1,r,rc));
	return res;
}
signed main(){
	n=read();
	for(int i=1;i<=n;++i)C=max(C,h[i]=read());
	for(int i=1;i<=n;++i)w[i]=read(),s[i]=s[i-1]+w[i];
	for(int i=1;i<=n;++i){
		if(i==1)dp[i]=0;
		else dp[i]=query(h[i])+1ll*h[i]*h[i]+s[i-1];
		li[++cnt]=mkp(-2*h[i],dp[i]-s[i]+1ll*h[i]*h[i]);
		update(cnt);
	}
	printf("%lld\n",dp[n]);
	return 0;
}

posted @ 2020-10-30 15:35  zzctommy  阅读(163)  评论(0编辑  收藏  举报