【BZOJ】2726: [SDOI2012]任务安排-斜率优化&CDQ二分

传送门:bzoj2726


题解

斜率优化。
按照“费用提前计算”的规则将每个任务的 S S S花费提前处理,列出 d p dp dp转移:
d p i = d p j − ( S + T i ) C j + T i C i + S C n dp_i=dp_j-(S+T_i)C_j+T_iC_i+SC_n dpi=dpj(S+Ti)Cj+TiCi+SCn
其中 C i = ∑ j = 1 i F j , T i = ∑ j = 1 i T j C_i=\sum\limits_{j=1}^iF_j,T_i=\sum\limits_{j=1}^iT_j Ci=j=1iFj,Ti=j=1iTj

转成斜率的形式:
F j = ( S + T i ) C j − T i C i − S C n + F i F_j=(S+T_i)C_j-T_iC_i-SC_n+F_i Fj=(S+Ti)CjTiCiSCn+Fi

因为 C j C_j Cj是单调的,其实可以在凸包上二分,cdq分治也可以做。


代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+100;
typedef long long ll;
typedef double db;

int n,S,top,stk[N];
int c[N],t[N];ll f[N];

struct P{
	int x;ll y;int k;ll b;int id;
	bool operator<(const P&ky)const{
	    return k<ky.k;
	}
}p[N],rep[N];

char cp;
template<class yyy>
inline void rd(yyy &x)
{
	cp=getchar();int pr=0;x=0;
	for(;!isdigit(cp);cp=getchar()) if(cp=='-') pr=1;
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
	if(pr) x=-x;
}

inline db slp(int A,int B)
{
	if(p[A].x==p[B].x) return (p[B].y>=p[A].y)?(1e18):(-1e18);
	return (p[B].y-p[A].y)/(db)(p[B].x-p[A].x);
}

void sol(int l,int r)
{
	if(l==r) {p[l].y=f[l];return;}
	int mid=(l+r)>>1,k,i=l,j=mid+1;
	for(k=l;k<=r;++k)
	 if(p[k].id<=mid) rep[i++]=p[k];
	 else rep[j++]=p[k];
	for(k=l;k<=r;++k) p[k]=rep[k];
	sol(l,mid);
    top=0;
    for(k=l;k<=mid;++k){
    	for(;top>1 && slp(stk[top-1],stk[top])>=slp(stk[top],k);--top);
    	stk[++top]=k;
    }
    for(k=mid+1,i=1;k<=r;++k){
    	for(;i<top && slp(stk[i],stk[i+1])<=(db)p[k].k;++i);
		f[p[k].id]=min(f[p[k].id],p[stk[i]].y-(ll)p[k].k*p[stk[i]].x+p[k].b);
    }
    sol(mid+1,r);
    i=l;j=mid+1;
    for(k=l;k<=r;++k)
     if((j>r) || (i<=mid && p[i].x<p[j].x)) rep[k]=p[i++];
     else rep[k]=p[j++];
    for(k=l;k<=r;++k) p[k]=rep[k];
}

int main(){
	int i,j;
	memset(f,0x7f,sizeof(f));
	f[0]=t[0]=c[0]=0;
	rd(n);rd(S);
	for(i=1;i<=n;++i){
		rd(t[i]);rd(c[i]);
		t[i]+=t[i-1];c[i]+=c[i-1];
	}
	ll res=(ll)S*c[n];
	for(i=1;i<=n;++i) p[i]=(P){c[i],0,S+t[i],(ll)t[i]*c[i]+res,i};
	sort(p+1,p+n+1);
	sol(0,n);
	printf("%lld\n",f[n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值