题意:
分析:
- 暴力
设 (f[i][j]) 表示枚举到第 (i) 个点,分成 (j) 段时的最小代价,
(f[i][j]=min(f[k][j-1]+(s*j+sumt[i]-sumt[k])*(sumc[i]-sumc[k])))
然后用李超树维护转移,复杂度 (O(n^2log n))
- 正解
在 巨佬 的提示下发现可以优化 DP 状态,我们发现 DP 状态的第二维可以通过更改枚举状态优化掉,因为他在转移式中只出现了一次,而且与 (k) 无关
我们设 (f[i]) 表示枚举到第 (i) 个点,之后的每一个点已经加上了由于之前重启操作带来的代价,转移式就变为了
(f[i]=min(f[j]+s*(sumc[n]-sumc[j])+sumt[i]*(sumc[i]-sumc[j])))
这样把之后每一个点由于之前操作带来的影响都消掉了
然后我们发现这个式子长的很斜率优化
我们令 (k< j) 且 (j) 比 (k) 转移更优,那么存在
(f[j]+s*(sumc[n]-sumc[j])+sumt[i]*(sumc[i]-sumc[j])<f[k]+s*(sumc[n]-sumc[k])+sumt[i]*(sumc[i]-sumc[k]))
化简得到
(f[j]-s*sumc[j]-sumt[i]*sumc[j]<f[k]-s*sumc[k]-sumt[i]*sumc[k])
(largefrac{(f[j]-s*sumc[j])-(f[k]-sumc[k])}{sumc[j]-sumc[k]}<sumt[i])
但是关键在于 (|t_i|<2^8) ,也就是说 (sumt[i]) 并不单调,所以我们不能单调队列维护,我们只能维护一个下凸壳,然后每次二分寻找最优决策点转移
复杂度 (O(nlog n))
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
using namespace std;
namespace zzc
{
inline long long read()
{
long long x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 3e5+5;
long long n,s,l,r;
long long f[maxn],c[maxn],ti[maxn],prec[maxn],pret[maxn],q[maxn];
long long upn(int j,int k)
{
return (f[j]-s*prec[j])-(f[k]-s*prec[k]);
}
long long dwn(int j,int k)
{
return (prec[j]-prec[k]);
}
int solve(long long x)
{
int res=-1,ll=l,rr=r-1,mid;
while(ll<=rr)
{
mid=(ll+rr)>>1;
if(upn(q[mid+1],q[mid])>x*dwn(q[mid+1],q[mid])) res=mid,rr=mid-1;
else ll=mid+1;
}
return res==-1?q[r]:q[res];
}
void work()
{
memset(f,0x7f,sizeof(f));
n=read();s=read();
for(int i=1;i<=n;i++) ti[i]=read(),c[i]=read(),prec[i]=prec[i-1]+c[i],pret[i]=pret[i-1]+ti[i];
l=0;r=0;f[0]=0;
for(int i=1;i<=n;i++)
{
int pos=solve(pret[i]);
f[i]=f[pos]+s*(prec[n]-prec[pos])+(prec[i]-prec[pos])*pret[i];
while(l<r&&dwn(i,q[r])*upn(q[r],q[r-1])>=dwn(q[r],q[r-1])*upn(i,q[r])) r--;
q[++r]=i;
}
printf("%lld
",f[n]);
}
}
int main()
{
zzc::work();
return 0;
}