决策单调性_笔记 gym 102904B Dispatch Money题解

决策单调性

判断决策单调性可以选择打表,有个比较好用的判断是这样的。

d p dp dp 转移式: f ( i ) = min ⁡ j < i { f ( j ) + c a l c ( j , i ) } f(i)=\min_{j<i}\{ f(j)+calc(j,i)\} f(i)=minj<i{f(j)+calc(j,i)}

如果任意 a < b a<b a<b 满足 c a l c ( a , b + 1 ) + c a l c ( a + 1 , b ) ≤ c a l c ( a , b ) + c a l c ( a + 1 , b + 1 ) calc(a,b+1)+calc(a+1,b)\le calc(a,b)+calc(a+1,b+1) calc(a,b+1)+calc(a+1,b)calc(a,b)+calc(a+1,b+1) ,则 f ( i ) f(i) f(i) 具有决策单调性。

决策单调性优化 d p dp dp
  • 分治做法

    处理 s o l v e ( l , r , q l , q r ) solve(l,r,q_l,q_r) solve(l,r,ql,qr) ,意思是 q l q_l ql ~ q r q_r qr 的决策点在 l l l ~ r r r 之中,现在要分治求 q l q_l ql ~ q r q_r qr 的决策点。

    m i d = q l + q 2 2 mid=\frac{q_l+q_2}{2} mid=2ql+q2 ,遍历 l l l ~ r r r 求出 m i d mid mid 的决策点 p p p

    递归处理 s o l v e ( l , p , q l , m i d − 1 ) solve(l,p,q_l,mid-1) solve(l,p,ql,mid1) s o l v e ( l , p , m i d + 1 , q r ) solve(l,p,mid+1,q_r) solve(l,p,mid+1,qr)

    缺点:有些情况只是遍历 l l l ~ r r r 难求出 m i d mid mid 的决策点,这时候要 c d q cdq cdq 分治里面套决这个分治 ,见例题。

  • 单调栈做法

    (我今天之前只会这种做法2333

    用单调栈维护若干个单元三元组 ( p , l , r ) (p,l,r) (p,l,r) 表示 l l l ~ r r r 的决策点为 p p p

    刚开始单调栈为空。

    每求出一个 f ( x ) f(x) f(x) ,就更新单调栈中的三元组,具体:从栈中取出一个三原组 ( p , l , r ) (p,l,r) (p,l,r) 然后 c h e c k check check f ( l ) f(l) f(l) x x x 转移过来更优还是 p p p 转移过来更优。如果 x x x 更优,就把这个三元组删除。否则就在 l l l ~ r r r 中二分出一个点 l ′ l' l 满足从 x x x 转移过来更优。最后把 ( x , l ′ , n ) (x,l',n) (x,l,n) 加入单调栈。

    P S PS PS :为什么可以这么做就不赘述了,其实挺显然的。

gym 102904B Dispatch Money

分治套分治做法+逆序对

#include <bits/stdc++.h>
#define N 300005
using namespace std;
typedef long long ll;
const ll inf=1e17;
int n,co,a[N],x=1,y,t[N]; 
ll ans[N],cnt;
int sum(int pos){ int res=0; for(;pos;pos-=pos&-pos) res+=t[pos]; return res; }
void add(int pos,int c){ for(;pos<=n;pos+=pos&-pos) t[pos]+=c; }
ll ask(int l,int r){
	while(x>l) cnt+=sum(a[x-1]), add(a[x-1],1), --x; 
	while(y<r) cnt+=y-x+1-sum(a[y+1]), add(a[y+1],1), ++y; 
	while(x<l) add(a[x],-1), cnt-=sum(a[x]), ++x; 
	while(y>r) add(a[y],-1), cnt-=y-x-sum(a[y]), --y; 
	return cnt; 
}
void solve(int l,int r,int ql,int qr){
	if(ql>qr)return;
	int mid=(ql+qr)>>1,v=0; ll now,m=inf;
	for(int i=l;i<=r&&i<mid;i++)
		if((now=(ans[i]+ask(i+1,mid)))<m) m=now,v=i; ans[mid]=min(m+co,ans[mid]);
	solve(l,v,ql,mid-1),solve(v,r,mid+1,qr);
}
void cdq(int l,int r){
	if(l==r)return;
	int mid=(l+r)>>1;
	cdq(l,mid);
	solve(l,mid,mid+1,r);
	cdq(mid+1,r);
}
int main(){
	cin>>n>>co;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),ans[i]=inf;
	cdq(0,n);
	cout<<ans[n]<<endl;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值