BZOJ 5125 小Q的书架 - 分治维护决策单调性dp - 学习笔记

121 篇文章 0 订阅
64 篇文章 0 订阅

决策单调性指的是,对于i< j,存在某个时刻k,满足i的转移不优于j,那么对于时刻t>k,i依然不会由于j。因此若将每个点的最优决策点(相同则最右)写下来,会是单调的。如果这个dp可以很快的计算一个决策点对于一个时刻的影响(即,如果转移方程是now[x]=min_{y < x}(pre[y]+calc(y+1,x)),而且calc函数可以很迅速的计算),那么可以用二分+单调栈维护。如果不是,但是影响可以类似莫队的办法增删两端的影响,就可以考虑分治维护。设solve(L,R,s,t)表示想要维护pre[L…R]对now[s…t]的影响,其中[L,R]表示对于所有的s<=x<=t,都有其最有决策点在[L,R]中。令mid=(s+t)/2,暴力找出mid的最优决策点p,然后用[L,p]更新[s,mid),右半部分同理。代码中的move(s,t)表示维护出calc(s,t)的答案v。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define K 15
#define N 40010
#define lint long long
#define gc getchar()
#define INF 1000000000
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int n,k,l,r,v,b[N],dp[K][N],a[N];
inline void update(int x,int v)
{   for(;x<=n;x+=(x&-x)) b[x]+=v;    }
inline int query(int x,int ans=0)
{   for(;x;x-=(x&-x)) ans+=b[x];return ans; }
inline void move(int s,int t)
{
    while(r<t) r++,v+=r-l-query(a[r]),update(a[r],1);
    while(l>s) l--,v+=query(a[l]),update(a[l],1);
    while(l<s) update(a[l],-1),v-=query(a[l]),l++;
    while(r>t) update(a[r],-1),v-=r-l-query(a[r]),r--;
//  debug(s)sp,debug(t)sp,debug(v)ln;
}
int solve(int *pre,int *now,int L,int R,int s,int t)
{
    if(s>t) return 0;int mid=(s+t)>>1,x=L;now[mid]=INF;
    for(int i=L;i<=min(mid-1,R);i++)
        move(i+1,mid),(pre[i]+v<now[mid]?now[mid]=pre[x=i]+v:0);
//  debug(L)sp,debug(R)sp,debug(s)sp,debug(t)sp,debug(x)sp,debug(mid)sp,debug(now[mid])ln;
    return solve(pre,now,L,x,s,mid-1),solve(pre,now,x,R,mid+1,t);
}
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int main()
{
    n=inn(),k=inn();
    for(int i=1;i<=n;i++)
        dp[1][i]=dp[1][i-1]+i-1-query(a[i]=inn()),update(a[i],1);
    for(int i=2;i<=k;i++)
        memset(b,0,sizeof(int)*(n+1)),v=0,
        l=1,r=0,solve(dp[i-1],dp[i],1,n,1,n);
    return !printf("%d\n",dp[k][n]);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值