bzoj3675: [Apio2014]序列分割

来源:互联网 发布:上海java培训公司名单 编辑:程序博客网 时间:2024/06/09 17:44

BZOJ3675 [Apio2014]序列分割

link:http://www.lydsy.com/JudgeOnline/problem.php?id=3675

简介

  网上大佬的题解都那么复(qi)杂(guai)。。。
  然而我做着感觉很轻松哎。。难道是我做错了?

题解

  fi,kik
  那么

fi,k=max{(sisj)×sj+fj,k1}

  为了好看把k这一维去掉,令gj=fj,k1fifi,k
  那么
fi=max{gj+(sisj)×sj}

  把max去掉得到
fi=(gjs2j)+si×sj

  那么fi就是截距,由于sj是单调不下降的所以把它作为横坐标,那么(gjs2j)就是纵坐标了
  为了好看再写一步,
  令xj=sjyj=(gjs2j)
  那么原方程化为
fi=si×xi+yi

  斜率(si)是单调不上升的,因此我们可以使用单调队列维护上凸壳,时间复杂度O(NK)
  有一个坑点:因为横坐标是单调不下降的,所以就有可能出现斜率不存在的直线,此时分母为0,应当特判

#include <cstdio>#include <algorithm>#define maxn 100010#define maxk 210#define ll long longusing namespace std;struct point{ll x, y;double k;}q[maxn];ll f[2][maxn], s[maxn], N, K;ll read(ll x=0){scanf("%lld",&x);return x;}void init(){    ll i, a;    N=read();K=read();    for(i=1;i<=N;i++)a=read(),s[i]=a+s[i-1];}void work(ll *f, ll *g, ll c){    ll l=1, r=1, i, x, y, flag;    q[r++]=(point){s[c],g[c]-s[c]*s[c],0};    for(i=c+1;i<=N;i++)    {        while(l<r-1 and -s[i]<q[l+1].k)l++;        f[i]=q[l].y+s[i]*q[l].x;        x=s[i],y=g[i]-s[i]*s[i];        if(x==q[r-1].x and y>q[r-1].y)r--;        else if(x==q[r-1].x and y<=q[r-1].y)continue;        while(r-1>l and double(y-q[r-1].y)/(x-q[r-1].x) > q[r-1].k)r--;        q[r]=(point){x,y,double(y-q[r-1].y)/(x-q[r-1].x)},r++;    }}int main(){    ll i;    init();    for(i=1;i<=K;i++)work(f[i&1],f[~i&1],i);    printf("%lld\n",f[K&1][N]);    return 0;}
0 0
原创粉丝点击