bzoj4167 永远亭的竹笋采摘

由于是随机数据,用分块加乱搞就可以水过。
详情参见大佬的博客:题解
贴个代码:

#include<cstdio> 
#include<cstring> 
#include<vector> 
#include<cmath> 
#include<algorithm> 
using namespace std; 
#define MAXN 50500 
#define MAXM 250 
int A[MAXN],n,k; 
const int inf=0x3c3c3c3c; 
void _r(int& x) 
{ 
    char c=getchar(); 
    while(c<'0'||c>'9') 
    { 
        c=getchar(); 
    } 
    for(x=0;c>='0'&&c<='9';c=getchar()) 
    { 
        x=(x<<1)+(x<<3)+c-'0'; 
    } 
    return ; 
} 
#define lowbit(x) (x&-x) 
int tr[MAXN]; 
void modify(int x,int d) 
{ 
    for(;x<=n;x+=lowbit(x)) 
    { 
        tr[x]=min(tr[x],d); 
    } 
    return ; 
} 
int get(int x) 
{ 
    int an=inf; 
    for(;x;x-=lowbit(x)) 
    { 
        an=min(an,tr[x]); 
    } 
    return an; 
} 
int size,dis[MAXN][MAXM],st[MAXN],top,num; 
int F[MAXN][MAXM*4+50]; 
vector<int>P[MAXN]; 
int cc=0;
void work(int pos,int nn,int lim,int& an) 
{ 
    int up=min(nn*size,n),tmp; 
    for(int i=lim;i<=up;i++) 
    { 
        tmp=abs(A[pos]-A[i]); 
        if(tmp>0&&tmp<an) 
        { 
            an=tmp; 
            if(an<get(i)) 
            { 
                modify(i,an); 
                //printf("%d  %d  %d\n",++cc,pos,i);
                P[i].push_back(pos); 
            } 
        } 
    } 
    return ; 
} 
int main() 
{   
    freopen("r.in","r",stdin);
    freopen("w.out","w",stdout);
    _r(n); 
    _r(k); 
    for(size=1;size*size<=n;++size); 
    num=(n-1)/size+1; 
    for(int i=1;i<=n;i++) 
    { 
        _r(*(A+i)); 
    } 
    memset(tr,0x3c,(n+1)<<2); 
    memset(dis,0x3c,sizeof(dis)); 
    int l,r; 
    for(int i=1;i<=num;i++) 
    { 
        l=(i-1)*size+1; 
        r=min(i*size,n); 
        top=0; 
        for(int j=l;j<=r;j++) 
        { 
            *(st+(++top))=A[j]; 
        } 
        sort(st+1,st+1+top); 
        l=0; 
        r=1; 
        for(int j=1;j<=n;j++) 
        { 
            while(l<top&&*(st+l+1)<j) 
            { 
                ++l; 
            } 
            while(r<=top&&*(st+r)<=j) 
            { 
                ++r; 
            } 
            if(l>0) 
            { 
                dis[j][i]=min(dis[j][i],j-st[l]); 
            } 
            if(r<=top) 
            { 
                dis[j][i]=min(dis[j][i],st[r]-j); 
            } 
        } 
    } 
    int pos,tmp; 
    for(int i=n;i>=1;i--) 
    { 
        pos=(i-1)/size+1; 
        tmp=inf; 
        for(int j=pos;j<=num;j++) 
        { 
            if(dis[A[i]][j]<tmp) 
            { 
                work(i,j,max(i,(pos-1)*size+1),tmp); 
            } 
        } 
    } 
    memset(F,0x3c,sizeof(F)); 
    F[0][0]=0;
    for(int i=1;i<=n;i++) 
    { 
        F[i][0]=0; 
        for(int j=1;j<=k;j++) 
        { 
            F[i][j]=F[i-1][j]; 
            for(int p=0;p<(int)P[i].size();p++) 
            { 
                F[i][j]=min(F[i][j],F[P[i][p]-1][j-1]+abs(A[i]-A[P[i][p]])); 
            } 
            /*if(F[i][j]==inf) 
            { 
                break; 
            } */
        } 
    } 
    printf("%d\n",F[n][k]); 
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值