斜率优化

模板

点击查看代码
struct CHT{
	int q[maxn];		//存决策点标号 
	ll X(int pos){/*	*/}
	ll Y(int pos){/*    */} 
	double get_K(int x,int y){
		return (double)(Y(x)-Y(y))/(double)(X(x)-X(y));
	} 
	int get_pos(int l,int r,int k){			//二分找到决策点 
		int ans=l;l++;
		while(r>=l){
			int mid=(l+r)>>1;
			if(get_K(q[mid],q[mid-1])<=k)ans=mid,l=mid+1;  //维护下凸包
			else r=mid-1; 
		}
		return q[ans];
	}
}T;

1. [HNOI2008]玩具装箱TOY
斜率优化

\(dp_i\)表示前\(i\)件玩具的最小费用,\(sum_i=\sum_{1\leq j\leq i}c_j\)

\(dp_i=min\{dp_j+[i-(j+1)+sum_i-sum_j-L]^2 \}\)

\(a_i=i+sum_i,b_j=j+1+sum_j+L\)

\(dp_i=min\{ dp_j+a_i^2-2*a_i*b_j+b_j^2 \}\)

\(dp_j+b_j^2=2*a_i*b_j+dp_i-a_i^2\)

形如\(Y=k*X+b\)

\(Y=dp_j+b_j^2,X=b_j,k=2*a_i\)

一般\(X\)\(Y\)都是决策状态,也就是与\(j\)相关的,我们要求的\(dp_i\)放在\(b\)里,这样我们只要求截距最小的就是当前\(dp_i\)的最小值

也就从之前的所有\((X,Y)\)中选一个点,使得经过那个点的直线截距最小,那么我们维护下凸包,然后二分找到那个点

因为这道题的每个i的斜率是单增的,也就是\(k=2*a_i\)是单增的,那么这道题我们也可以不用二分,直接维护队首的元素,把不优的直接弹出

点击查看代码
#include <bits/stdc++.h>
#define ll long long 
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=1e9+7;
const ll inf=2147483647;
const double eps=1e-12;
ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}

ll n,L,c[maxn],sum[maxn],dp[maxn],a[maxn],b[maxn];
struct CHT{
	int q[maxn];		//存决策点标号 
	ll X(int pos){return b[pos];}
	ll Y(int pos){return dp[pos]+b[pos]*b[pos];} 
	double get_K(int x,int y){
		return (double)(Y(x)-Y(y))/(double)(X(x)-X(y));
	} 
	int get_pos(int l,int r,int k){			//二分找到决策点 
		int ans=l;l++;
		while(r>=l){
			int mid=(l+r)>>1;
			if(get_K(q[mid],q[mid-1])<=k)ans=mid,l=mid+1;
			else r=mid-1; 
		}
		return q[ans];
	}
}T;
int main(){
	n=read();L=read();b[0]=L+1;
	for(int i=1;i<=n;i++){
		c[i]=read();
		sum[i]=sum[i-1]+c[i];
		a[i]=sum[i]+i;
		b[i]=i+1+sum[i]+L;
	}
	int tail=1,head=1;dp[1]=(a[1]-b[0])*(a[1]-b[0]);
	for(int i=2;i<=n;i++){
		int id=T.get_pos(head,tail,2*a[i]); 	
		dp[i]=dp[id]+(a[i]-b[id])*(a[i]-b[id]);
		while(head<tail && T.get_K(T.q[tail],T.q[tail-1])>T.get_K(T.q[tail-1],i))tail--;
		T.q[++tail]=i;
    }
    printf("%lld\n",dp[n]);
	return 0;
}

2.E. Long Way Home

题解

点击查看代码
#include <bits/stdc++.h>
#define ll long long 
#define pa pair<ll,ll>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=1e9+7;
const ll inf=1e17;
const double eps=1e-12;
ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
ll n,m,K;
ll dp[21][maxn];

struct CHT{
	ll q[maxn];		//存决策点标号 
	ll now;			//存第几层 
	ll X(ll pos){return pos;}
	ll Y(ll pos){return dp[now][pos]+pos*pos;} 
	double get_K(ll x,ll y){
		return (double)(Y(x)-Y(y))/(double)(X(x)-X(y));
	} 
	int get_pos(ll l,ll r,ll k){			//二分找到决策点 
		ll ans=l;l++;
		while(r>=l){
			ll mid=(l+r)>>1;
			if(get_K(q[mid],q[mid-1])<=k)ans=mid,l=mid+1;
			else r=mid-1; 
		}
		return q[ans];
	}
}T;
vector<vector<pa> >G(maxn+1);
int vis[maxn];
struct node{
	ll dis,pos;
	bool operator <(const node &x)const{
		return x.dis<dis;
	} 
};
void Dij(int now){
	for(int i=1;i<=n;i++)vis[i]=0;
	priority_queue<node>que;
	for(int i=1;i<=n;i++){
		que.push({dp[now][i],i});
	}
	while(que.size()){
		auto x=que.top();que.pop();
		ll u=x.pos,dis=x.dis;
		if(vis[u])continue;
		vis[u]=1;
		for(auto ed:G[u]){
			ll v=ed.fi,w=ed.se;
			if(dp[now][v]>dis+w){
				dp[now][v]=dis+w;
				if(!vis[v])que.push({dp[now][v],v});
			}
		}
	}
	return ;
} 
int main(){
	n=read();m=read();K=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),z=read();
		G[x].pb(mp(y,z));
		G[y].pb(mp(x,z)); 
	}
	for(int k=0;k<=K;k++)for(int u=1;u<=n;u++)dp[k][u]=1e16;
	dp[0][1]=0;Dij(0);
	int tail,head;
	for(int k=1;k<=K;k++){ 
		T.now=k-1;head=1;tail=0; 
		for(ll u=1;u<=n;u++){
			while(tail>head && T.get_K(T.q[tail-1],T.q[tail])>=T.get_K(u,T.q[tail-1]))tail--;
			T.q[++tail]=u;
		}
		for(ll u=1;u<=n;u++){
			ll v=T.get_pos(head,tail,2*u);
			dp[k][u]=min(dp[k][u],dp[k-1][v]+(u-v)*(u-v));
		}
		Dij(k);
	}
	for(int i=1;i<=n;i++){
		ll ans=inf;
		for(int j=0;j<=K;j++)ans=min(ans,dp[j][i]);
		cout<<ans<<" ";
	}
	return 0;
}
posted @ 2022-08-25 23:05  I_N_V  阅读(17)  评论(0编辑  收藏  举报