斜率优化——炸铁轨

在一条直线上,有n个火车站,第i条铁轨连接着第i个和第i+1个火车站。
每个火车站都有一个花费wi。
定义所有火车站的总花费为∑i和j可以互相到达wiwj。
比如有4个火车站,权值分别为4,5,1,2。
如图:4 — 5 — 1 — 2,总花费为:4*5 + 4*1 + 4*2 + 5*1 + 5*2 + 1*2 = 49
现在给你机会去炸掉m条铁轨,希望你能让火车站的总花费最小。
假设m=1,你可以炸掉5 — 1这条铁轨,变成了这样: 4 — 5 -X- 1 — 2,总花费为4*5 + 1*2 = 22。
但是你炸4 — 5这条更优,因为总花费变成了5*1 + 5*2 + 1*2 = 17。

输入格式:
第一行,两个整数n,m(0≤m

%:pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define N 2001
#define r(x) scanf("%d",&x)
#define pln(x) printf("%d\n",x)
#define pk(x) printf("%d ",x)
#define p(x) printf("%d",x)
#define PER(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
int n,m;
long long dp[N][N];
long long a[N];
long long S[N];
long long sq[N];
long long d[N][N];
long long G[N][N];
int q[N+2],h,t;
long long up(int x,int y,int kk)
{
    return (dp[x][kk-1]-dp[y][kk-1])+S[x]*S[x]-S[y]*S[y];
}
long long down(int x,int y,int kk)
{
    return 2*(S[x]-S[y]);
}
//long long getdp(int x,int y,int xx){return dp[xx][y]+G[xx+1][x];}
int main()
{
    r(n);r(m);
    PER(i,1,n) r(a[i]);
    PER(i,1,n) sq[i]=sq[i-1]+a[i]*a[i];
    PER(i,1,n) S[i]=S[i-1]+a[i];
//    PER(i,1,n) cout<<S[i]<<" ";
    PER(i,1,n) PER(k,1,i) G[k][i]=(S[i]-S[k-1])*(S[i]-S[k-1]);
    PER(i,1,n) dp[i][0]=G[1][i];
    PER(j,1,m)
    {
        memset(q,0,sizeof(q));
        h=t=0;
//      h=1
        q[++t]=0;
        PER(i,1,n)
        {
            while(h<t&&up(q[h+1],q[h],j)<=S[i]*down(q[h+1],q[h],j)) h++;
            dp[i][j]=dp[q[h]][j-1]+(S[i]-S[q[h]])*(S[i]-S[q[h]]);
//            if(i==2&&j==1) cout<<dp[q[h]][j-1]<<" "<<S[2]<<" "<<(S[i]-S[q[h]+1])*(S[i]-S[q[h]+1]);
//            if(j==1) continue;
            while(h<t&&up(q[t],q[t-1],j)*down(i,q[t],j)>=up(i,q[t],j)*down(q[t],q[t-1],j)) t--;
            q[++t]=i;
        }
    }
//    PER(i,1,n){puts("");PER(j,0,m)cout<<dp[i][j]<<" ";}
    cout <<(dp[n][m]-sq[n])/2<< endl;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值