1. 程式人生 > >LOJ2483. 「CEOI2017」Building Bridges (李超樹+DP)

LOJ2483. 「CEOI2017」Building Bridges (李超樹+DP)

LOJ2483. 「CEOI2017」Building Bridges (李超樹+DP)

題意

有n個建築,每個建築有兩個權值(h[i],w[i]) ,h[i]表示建築的高度,w[i]表示拆除建築的費用.
現在要在除了頭尾之外的n-2個建築內選擇若干個保留,並且保留頭尾的建築.
這樣的費用為拆除所有沒有保留建築的費用+相鄰的保留兩個建築高度差的平方.
求最小費用.

分析

首先有一個簡單的dp思路
dp[i] 表示保留建築i的最小費用.
s u

m [ k ] = i = 1
k
w [ i ] sum[k]=\sum_{i=1}^{k}w[i]

初始化:dp[1]=0
答案:dp[n]
轉移方程:
d

p [ i ] = m i n ( d p [ j ] + ( s u m [ i 1 ] s u m [ j ] ) + ( h [ i ] h [ j ] ) 2 ) dp[i]=min(dp[j]+(sum[i-1]-sum[j])+(h[i]-h[j])^2)

然後就能獲得40分的好成績

然後來處理一下這個式子.
展開
d p [ j ] + s u m [ i 1 ] s u m [ j ] + h [ i ] h [ i ] + h [ j ] h [ j ] 2 h [ i ] h [ j ] dp[j]+sum[i-1]-sum[j]+h[i]*h[i]+h[j]*h[j]-2*h[i]*h[j]
整理
d p [ j ] s u m [ j ] + h [ j ] h [ j ] 2 h [ i ] h [ j ] + s u m [ i 1 ] + h [ i ] h [ i ] dp[j]-sum[j]+h[j]*h[j] -2*h[i]*h[j] +sum[i-1]+h[i]*h[i]
然後就很斜率優化了.


k = h [ j ] b = d p [ j ] s u m [ j ] + h [ j ] h [ j ] k=h[j]\\ b=dp[j]-sum[j]+h[j]*h[j]

每次查詢就是給出一個x
x = 2 h [ i ] x=-2*h[i]

一個解決方法是CDQ分治
[但是我寫不動
一個解決方法是treap
[但是我不會

可以發現需要操作的只有插入線和詢問點的最大值.
然後就直接李超樹維護一下,區間範圍為[-2e6,0]

之後就沒有了.
因為是全域性插入所以插入線複雜度是log的.[然而如果是固定區間更新的話,複雜度是 l o g 2 log^2
而查詢單點是 l o g log 的.
所以複雜度是 n log n n\log n

實現比較容易而且跑得快.
[至於李超樹怎麼操作請去看別的部落格 寫的都挺好的qwq?

code

為什麼沒有高亮

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define pb push_back
#define A first 
#define B second
#define pii pair<int,int>
#define lowbit(p) (p&(-(p)))
using namespace std;
void read(int &x){
	x=0; char c=getchar(); int p=1;
	for (;c<48;c=getchar())if (c=='-')p=-1;
	for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
	x*=p;
}
void read(ll &x){
	x=0; char c=getchar(); int p=1;
	for (;c<48;c=getchar())if (c=='-')p=-1;
	for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
	x*=p;
}
void Min(int &x,int y){
	if (x>y)x=y;
}
void Min(ll &x,ll y){
	if (x>y)x=y;
}
void Max(int &x,int y){
	if (x<y)x=y;
}
void Max(ll &x,ll y){
	if (x<y)x=y;
}

#define M 100005
const ll inf=1e17;
ll h[M],dp[M],sum[M],w[M];
int n;
struct P1{
	void solve(){
		int i,j;
		for (i=2;i<=n;i++){
			dp[i]=inf;
			for (j=1;j<i;j++){
				Min(dp[i],dp[j]+(sum[i-1]-sum[j])+(h[j]-h[i])*(h[j]-h[i]));
			}
		}
		printf("%lld\n",dp[n]);
	}
}p1;
inline ll chk(ll k,ll b,ll x){
	return k*x+b;
}
#define N 2000005
int L,R,rt;
struct Reimu{
	ll kk[N],bb[N];
	int tot,ls[N],rs[N];
	void upd(int l,int r,ll k,ll b,int &p){
		if (!p){
			p=++tot;
			kk[p]=k;
			bb[p]=b;
			return;
		}
		if (chk(k,b,l)<=chk(kk[p],bb[p],l)&&chk(k,b,r)<=chk(kk[p],bb[p],r)){
			kk[p]=k;
			bb[p]=b;
			return ;
		}
		if (chk(k,b,l)>chk(kk[p],bb[p],l)&&chk(k,b,r)>chk(kk[p],bb[p],r)){
			return ;
		}
		if (l==r){
			return ;
		}
		int mid=(l+r)>>1;
		ll lk=kk[p],lb=bb[p];
		if (chk(k,b,l)<=chk(lk,lb,l)){
			if (chk(k,b,mid)<=chk(lk,lb,mid)){
				kk[p]=k;
				bb[p]=b;
				upd(mid+1,r,lk,lb,rs[p]);
				return ;
			}
			upd(l,mid,k,b,ls[p]);
			return ;
		}
		else{
			if (chk(k,b,mid+1)<=chk(lk,lb,mid+1)){
				kk[p]=k;
				bb[p]=b;
				upd(l,mid,lk,lb,ls[p]);
			}
			upd(mid+1,r,k,b,rs[p]);
		}
	}
	ll qu(int l,int r,int x,int p){
		if (!p)return inf;
		int mid=(l+r)>>1;
		if (x<=mid)return min(chk(kk[p],bb[p],x),qu(l,mid,x,ls[p]));
		return min(chk(kk[p],bb[p],x),qu(mid+1,r,x,rs[p]));
	}
}T;
struct P2{
	ll qu(ll x){
		return T.qu(L,R,x,rt);
	}
	void ins(ll k,ll b){
		T.upd(L,R,k,b,rt);
	}
	void solve(){
		int i;
		L=-2e6-5;
		R=5;
	/*
	
b=(dp[j]-sum[j]+h[j]*h[j])
k=h[j]

x=-2*h[i]
	*/
		ins(h[1],dp[1]-sum[1]+h[1]*h[1]);
		for (i=2;i<=n;i++){
			dp[i]=qu(-2*h[i])+h[i]*h[i]+sum[i-1];
			ins(h[i],dp[i]-sum[i]+h[i]*h[i]);
		}
		printf("%lld\n",dp[n]);
	}
}p2;

int main(){
//	freopen("1.in","r",stdin);
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n);
	int i;
	for (i=1;i<=n;i++){
		read(h[i]);
	}
	for (i=1;i<=n;i++){
		read(w[i]);
		sum[i]=sum[i-1]+w[i];
	}
	if (n<=1000){
		p1.solve();
		return 0;
	}
	p2.solve();
	return 0;
}