[BZOJ1150][CTSC2007]數據備份Backup(DP凸優化/wqs二分)

題目:

我是超鏈接

題解:

首先我們可以列出一個60pts的DP式
f[0/1][i][j] 表示i和i-1有沒有相連,前i個分成j組的最小總長
那麼轉移很簡單
f[0][i][j]=min(f[0][i1][j],f[1][i1][j])
f[1][i][j]=f[0][i1][j1]+s[i]s[i1]
但是這樣n^2肯定過不去
我們覺得很像省選一輪的D2T2
現在發現隨着j的增加,這個函數的值一定是增的(廢話),並且是下凸的,這也不難理解,因爲我們一開始肯定是先選增值小的,然後選增加大的

考慮二分把枚舉j的一維省略,我們按照套路,使每分一組獲得一個代價來控制選的組數量
特別注意有切不到的情況,那麼在<=k的時候我們都要更新答案

代碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long 
using namespace std;
const int N=100005;
int s[N],n;LL mid;
struct hh{LL x,y;}f[2][N];
hh operator +(hh a,int b){return (hh){a.x+b,a.y};}
bool operator <(hh a,hh b){return a.x<b.x;}
hh add(hh a){return (hh){a.x-mid,a.y+1};}
void work()
{
    memset(f,0x7f,sizeof(f));
    f[0][1].x=0; f[0][1].y=0;
    for (int i=2;i<=n;i++)
    {
        f[0][i]=min(f[0][i-1],f[1][i-1]);
        f[1][i]=add(f[0][i-1]+(s[i]-s[i-1]));
    }
    f[0][n]=min(f[1][n],f[0][n]);
}
int main()
{
    int K;scanf("%d%d",&n,&K);
    LL l=0,r=0,ans=0;
    for (int i=1;i<=n;i++) scanf("%d",&s[i]),r+=(LL)s[i];
    while (l<=r)
    {
        mid=(l+r)>>1;
        work();
        if (f[0][n].y<=K) ans=f[0][n].x+(LL)K*mid,l=mid+1;else r=mid-1;//r=mid-1;else l=mid+1;
    }
    printf("%lld",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章