
本文共 2394 字,大约阅读时间需要 7 分钟。
决策单调性
判断决策单调性可以选择打表,有个比较好用的判断是这样的。
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,mid−1) , 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 300005using 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;}
发表评论
最新留言
关于作者
