BZOJ 2726 SDOI2012 任务安排

本文详细解析了BZOJ中的一个特定问题,并提出了基于斜率优化的解决方案,通过维护下凸包并进行二分查找,将时间复杂度从O(n^2)优化至O(nlogn)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem

BZOJ

Solution

不难设出一个不怎么样的状态f[i][j]表示前i个分j段的最小代价

f [ i ] [ j ] = min ⁡ ( f [ k ] [ j − 1 ] + ( S ∗ j + s t [ i ] ) ∗ ( s f [ i ] − s f [ k ] ) ) f[i][j]=\min(f[k][j-1]+(S*j+st[i])*(sf[i]-sf[k])) f[i][j]=min(f[k][j1]+(Sj+st[i])(sf[i]sf[k]))

考虑未来费用优化,多分一段相当于后面所有段都加上S×费用

f [ i ] = min ⁡ ( f [ j ] + s t [ i ] ∗ ( s f [ i ] − s f [ j ] ) + S ∗ ( s f [ n ] − s f [ j ] ) ) f[i]=\min(f[j]+st[i]*(sf[i]-sf[j])+S*(sf[n]-sf[j])) f[i]=min(f[j]+st[i](sf[i]sf[j])+S(sf[n]sf[j]))

f [ i ] = s t [ i ] + s f [ i ] + S ∗ s f [ n ] + min ⁡ ( f [ j ] − s t [ i ] ∗ s f [ j ] − S ∗ s f [ j ] ) ) f[i]=st[i]+sf[i]+S*sf[n]+\min(f[j]-st[i]*sf[j]-S*sf[j])) f[i]=st[i]+sf[i]+Ssf[n]+min(f[j]st[i]sf[j]Ssf[j]))

时间复杂度 O ( n 2 ) O(n^2) O(n2)

发现不知道怎么单调队列,所以试试考虑斜率优化,若i从j转移比从k转移要优,则有:
f [ j ] − f [ k ] + s t [ i ] ∗ ( s f [ k ] − s f [ j ] ) + S ∗ ( s f [ k ] − s f [ j ] ) &lt; 0 f[j]-f[k]+st[i]*(sf[k]-sf[j])+S*(sf[k]-sf[j])&lt;0 f[j]f[k]+st[i](sf[k]sf[j])+S(sf[k]sf[j])<0

f [ j ] − f [ k ] &lt; ( s t [ i ] + S ) ( s f [ j ] − s f [ k ] ) f[j]-f[k]&lt;(st[i]+S)(sf[j]-sf[k]) f[j]f[k]<(st[i]+S)(sf[j]sf[k])

f [ j ] − f [ k ] s f [ j ] − s f [ k ] &lt; s t [ i ] + S \frac {f[j]-f[k]} {sf[j]-sf[k]}&lt;st[i]+S sf[j]sf[k]f[j]f[k]<st[i]+S

维护一个下凸包。又由于st[i]+s不一定单调,所以不能用单调队列,而是在维护好的凸包上二分。

注意特判sf[j]和sf[k]相等的时候,f小的更优秀。不过好像写的代码不同也有所不同,不然两种都试一下吧。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

Code

#include <cstdio>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=1000010;
const double INF=1e20;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
	x=0;int f=0;char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=1,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	if(f) x=-x;
}
int n,s,tp,st[maxn],sf[maxn],stk[maxn];
ll f[maxn];
double slope(int j,int k)
{
	if(sf[j]==sf[k]) return f[j]<f[k]?INF:-INF;
	return 1.0*(f[j]-f[k])/(sf[j]-sf[k]);
}
int find(int k)
{
	int l=1,r=tp,m,res=1;
	while(l<=r)
	{
		m=(l+r)>>1;
		if(slope(stk[m-1],stk[m])<=k) res=m,l=m+1;
		else r=m-1;
	}
	return stk[res];
}
int main()
{
	read(n);read(s);
	for(rg int i=1;i<=n;i++)
	{
		read(st[i]);st[i]+=st[i-1];
		read(sf[i]);sf[i]+=sf[i-1];
	}
	stk[++tp]=0;
	for(rg int i=1;i<=n;i++)
	{
		int k=find(st[i]+s);
		f[i]=f[k]+(ll)st[i]*(sf[i]-sf[k])+(ll)s*(sf[n]-sf[k]);
		while(tp>1&&slope(stk[tp-1],stk[tp])>=slope(stk[tp],i)) tp--;
		stk[++tp]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值