题目
你有n个任务和一台机器,第i个任务完成时间是s[i],带来的费用是它的完成时间*费用系数f[i]。
每次启动机器需要时间S,可以将几个任务分成一组,这样只需要启动一次机器,但是所有任务的完成时间都将被计算为最后一个任务完成的时间。
注意计算等待时间!!!
现在你需要使费用最小。
时间可以为负数
0 < N<=300000 0<=S<=2^8 -(2^8)<=Ti<=2^8 0<=Fi<=2^8
分析
朴素方程
f(i,j)表示将前i个元素分成j组的最小费用。
f(i,j)=min{ f(k,j-1) + ( s * j + sumT [i] )( sumF[i]-sumF[k]) | 0<=k< I }
sumT表示T的前缀和
sumF表示F的前缀和
时间复杂度O(n^3)
空间复杂度O(n^2)
会炸的
进阶版方程
假设我们分好了组:
第i组完成的时间是ai+i*s,第i组的费用系数和是bi。
Ans=(a1+s)*b1+(a2+2*s)*b2+…+(am+m*s)*bm
集中注意力辣!!
Ans=(a1*b1+a2*b2+…+am*bm)+(s*b1+s*2*b2+..s*m*bm)
=(a1*b1+a2*b2+…+am*bm)+s*( (b1+…+bm-1+bm) +…+(bm+1+bm)+(bm))
其中a其实是原T的前缀和,b那一堆可以处理成后缀和。
那么令sumT为T的前缀和,sumrF为F的后缀和,有转移方程如下:
F(i)=min{f(j)+sumT(i)*( sumrF(j+1)-sumrF(i+1) )+s*sumrF(j+1)|0<=j
斜率优化
define st sumT
define sf sumrF
令t1<=t2< I,则当t2为更优决策时,有:
F(t2)-F(t1)/(sf(t2+1)-sf(t1+1))>=-st[i]-s//sf(t2+1)-sf(t1+1))<0变号
则令y[i]=F(i),x[i]=sf(i+1)
x有单调性
所以维护下凸包即可,然后三分找答案。
注意在考虑的时候,由于X单调递减,所以所谓的往后一个元素实际上在图的位置上是往前一个元素,卡了好久哦。
这里最好用叉积(结合long doulbe),否则会炸掉
然后还可以用CDQ来做,这里我强行CDQ了一波(时间多了个log),随机数据挺快的,但是会被卡!!我的第一道CDQ啊啊啊啊,过都过不了。
代码
三分版
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
const int maxn=3e5+105;
const LL inf=1e18;
int n,s;
int t[maxn],f[maxn],st[maxn],sf[maxn];
LL d[maxn],x[maxn],y[maxn];
void Init()
{
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&t[i],&f[i]);
st[i]=st[i-1]+t[i];
}
for(int i=n;i>=1;i--)
sf[i]=sf[i+1]+f[i];
}
int q[maxn],front,rear;
#define check(x) d[x]+1ll*st[i]*(sf[x+1]-sf[i+1])+1ll*s*sf[x+1]
LL sanfen(int l,int r,int i)
{
LL ret=inf;
int mid1,mid2;
while(r-l>2)
{
mid1=l+(r-l)/3;
mid2=r-(r-l)/3;
if(check(q[mid1])<check(q[mid2]))
r=mid2;
else
l=mid1;
}
for(int x=l;x<=r;x++)
ret=min(ret,check(q[x]));
return ret;
}
#define calc(a,b,c) (ld)(x[b]-x[a])*(y[c]-y[a])-(ld)(x[c]-x[a])*(y[b]-y[a])
void dp()
{
front=rear=0;
q[rear++]=0;
x[0]=sf[1];
for(int i=1;i<=n;i++)
{
d[i]=sanfen(front,rear-1,i);
x[i]=sf[i+1],y[i]=d[i];
while(rear-front>1 && calc(q[rear-2],q[rear-1],i)>=0)rear--;
q[rear++]=i;
}
printf("%lld\n",d[n]);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out1.txt","w",stdout);
Init();
dp();
return 0;
}
过不了的CDQ
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
const int maxn=3e5+105;
const LL inf=1e18;
int n,s;
int t[maxn],f[maxn],st[maxn],sf[maxn];
LL d[maxn],x[maxn],y[maxn];
namespace IStream{
const int L=1<<15;
char buffer[L],*S,*T;
inline char Get_Char()
{
if(S==T)
{
T=(S=buffer)+fread(buffer,1,L,stdin);
if(S==T) return EOF;
}
return *S++;
}
inline void Rd(int &re)
{
char c;
re=0;
int k=1;
for(c=Get_Char();(c<'0'||c>'9') && c!='-';c=Get_Char());
if(c=='-')c=Get_Char(),k=-1;
while(c>='0'&&c<='9')
re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
re*=k;
}
}
void Init()
{
IStream::Rd(n);IStream::Rd(s);
for(int i=1;i<=n;i++)
{
IStream::Rd(t[i]);IStream::Rd(f[i]);
st[i]=st[i-1]+t[i];
}
for(int i=n;i>=1;i--)
sf[i]=sf[i+1]+f[i];
}
int a[maxn],tmp[maxn];
int q[maxn],front,rear;
#define check(x) d[x]+1ll*st[i]*(sf[x+1]-sf[i+1])+1ll*s*sf[x+1]
LL sanfen(int l,int r,int i)
{
LL ret=inf;
int mid1,mid2;
while(r-l>2)
{
mid1=l+(r-l)/3;
mid2=r-(r-l)/3;
if(check(q[mid1])<check(q[mid2]))
r=mid2;
else
l=mid1;
}
for(int x=l;x<=r;x++)
ret=min(ret,check(q[x]));
return ret;
}
//a数组表示当前第i个位置的元素
#define calc(a,b,c) (ld)(x[b]-x[a])*(y[c]-y[a])-(ld)(x[c]-x[a])*(y[b]-y[a])
void CDQ(int l,int r)
{
if(r-l==1)
{
x[a[l]]=sf[a[l]+1];
y[a[l]]=d[a[l]];
return;
}
int mid=(l+r)>>1;
CDQ(l,mid);
front=rear=0;
for(int i=l;i<mid;i++)
{
while(rear-front>1 && calc(q[rear-2],q[rear-1],a[i])<=0)rear--;
q[rear++]=a[i];
}
for(int i=mid;i<r;i++)
{
d[a[i]]=min(d[a[i]],sanfen(front,rear-1,a[i]));
}
CDQ(mid,r);
int i=l,j=mid,k=0;
while(i<mid && j<r)tmp[k++]=x[a[i]]<x[a[j]]?a[i++]:a[j++];
while(i<mid)tmp[k++]=a[i++];
while(j<r)tmp[k++]=a[j++];
for(int i=0;i<k;i++)a[l+i]=tmp[i];
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out1.txt","w",stdout);
Init();
for(int i=1;i<=n;i++)a[i]=i,d[i]=inf;
CDQ(0,n+1);
printf("%lld\n",d[n]);
return 0;
}