luogu P3195 https://www.luogu.org/problem/show?pid=3195
BZOJ 1010 http://www.lydsy.com/JudgeOnline/problem.php?id=1010
状态转移方程是显然的:
f[i]=min{f[j]+(i-j-1+s[i]-s[j]-L)^2},0<=j
这是一个1D/1D的DP问题。不优化的话是O(n^2)的,肯定会TLE。
考虑优化。显然可以换成斜率优化一般形式:
斜率优化一般形式:f[i]=min{ai*bj+ci+dj}.//这里ai,ci都是一开始就能O(1)确定的,bj,dj都是算出f[j]后可以O(1)确定的
f[i]=min{-2(i+s[i])(j+s[j]+L+1)+(i+s[i])^2+(j+s[j]+L+1)^2+f[j]} //这一步通过代数运算,把多项式弄成(只和i有关)*(只和j有关)+(只和i有关)+(只和j有关)的形式。
所以在这道题目中,ai=-2*(i+s[i]);
bj=i+s[i]+L+1;ci=(j+s[j])^2;
dj=(i+s[i]+L+1)^2+f[j];
然后考虑:对于要求的状态f [ i ],如果状态j比k更优(不妨设bjai*bj+ci+dj<=ai*bk+ci+dk
化简整理得到:
-ai<=(dk-dj)/(bk-bj).
这里把(dk-dj)/(bk-bj)记作Kjk(规定b小的在前面),因为K的形式是个斜率,由此可知可以把b,d分别看作横纵坐标。
即当-ai<=Kjk的时候,从状态j转移更优。
然后继续考虑:
如果bx=Kyz
如果存在-ap<=Kxy,则从状态x转移更优
否则若-ap>Kxy>=Kyz,则从状态z转移更优
综上,若bx=Kyz,那么无论什么时候,都不会有从y转移最优的情况(注意这里并没有用到-ap)。因此可以把y删除。
所以我们要维护一些点,使得相邻的斜率时递增的。也就是一个下凸包。
一般意义上的下凸包维护起来比较麻烦,所以还要具体分析(见下文)。
那么如何找到最优转移状态呢?
考虑(由于K已经是递增的了):正在计算的状态是i,且Kxy<-ai<=Kyz,其中xyz是相邻的。显然因为K是单调递增的,这样的不等式最多只有一个。由左边的等号知:从状态y转移更优。由右边的等号知:从状态y转移更优。
于是我们就是在一个单调递增的数列中找-ai的bound(分不清是upper还是lower),这个总复杂度是O(nlgn)的。
还要注意的是有可能这个等会不存在的情况,需要特判。
当然也不是绝对的,比如说这道题,总复杂度可以做到O(n).
对这道题的具体分析:
首先,bj=j+s[j]+L+1是单调递增的。所以我们每次只是在右边加点,可以用一个单调栈维护。
每个元素进栈出栈最多各一次,总复杂度是O(n)的
其次-ai=2*(i+s[i])是单调递增的。即我们询问的值也是单调递增的。所以在序列左端可以维护一个指针,单调移动。
综上可以维护一个单调队列,左端支持删除,右端支持插入即可。
算法的总复杂度就是O(n).
附上代码,有一些特判比较丑陋。另外,这个题的数据比较大,所以用longlong会超界。
在判断两个斜率相称的时候只能用double类型通过除法来判断。(为此WA了十几次)
堵上丑陋的代码:
#include
#include
#include
#define debug(x) cerr<<#x<<'='<1.0) return 1;
else return -1;
}
inline int kcmp(ll k,ll x1,ll y1,ll x2,ll y2)
{
double s=(double)k/(y2-y1)*(x2-x1);
if(s>1.0) return 1;
else return -1;
}
struct Deque{
private:
ll qx[MAXN+10];
ll qy[MAXN+10];
int fp,rp;
public:
Deque()
{
memset(qx,0,sizeof(qx));
memset(qy,0,sizeof(qy));
fp=1;rp=0;
}
int push(ll x,ll y)
{
if(fp==rp||fp==rp+1)
{ rp++;qx[rp]=x;qy[rp]=y;return 0; }
while(fp!=rp&&kcmp(qx[rp-1],qy[rp-1],qx[rp],qy[rp],x,y)>=0)//kxy>=kyz
rp--;
rp++;qx[rp]=x;qy[rp]=y;return 0;
}
ll query(ll k,ll ci)//k=-ai
{
ll bj,dj;
if(fp==rp+1)
{
bj=L+1;dj=(ll)(L+1)*(L+1);
return -k*bj+ci+dj;
}
if(fp==rp)
return min(-k*qx[fp]+ci+qy[fp],-k*(L+1)+ci+(ll)(L+1)*(L+1));
while(fp!=rp&&kcmp(k,qx[fp],qy[fp],qx[fp+1],qy[fp+1])>0)//k>kxy
fp++;
return min(-k*qx[fp]+ci+qy[fp],-k*(L+1)+ci+(ll)(L+1)*(L+1));
}
}deq;
int main()
{
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++)
{
int input_number;
scanf("%d",&input_number);
s[i]=s[i-1]+input_number;
}
ll ai,ci,bj,dj,ans;
for(int i=1;i<=n;i++)
{
ai=-2*(i+s[i]);
ci=(i+s[i])*(i+s[i]);
ans=deq.query(-ai,ci);
bj=i+s[i]+L+1;
dj=(i+s[i]+L+1)*(i+s[i]+L+1)+ans;
deq.push(bj,dj);
}
printf("%lld\n",ans);
return 0;
}