【dp-关于决策点】[Lydsy12月赛] BZOJ5124波浪序列 BZOJ5125小Q的书架
2018-02-22 21:20
302 查看
其实本来是不想写这篇博文的,但是5124这题没见过想写,单独写又有点短,于是乎多写一个凑数。
还有下面的原题地址是没有题面的,题面在这里。
原题地址
给定两个XX维向量序列a[1,n],b[1,m]a[1,n],b[1,m],求有多少个序列f,gf,g满足1≤f1<f2<...<fk≤n,1≤g1<g2<...<gk≤m1≤f1<f2<...<fk≤n,1≤g1<g2<...<gk≤m且afi=bfi,[af1,af2,...,afk]afi=bfi,[af1,af2,...,afk]是波浪的(波浪指对于每个非两端ii满足ai−1<ai>ai+1ai−1<ai>ai+1或ai−1>ai<ai+1ai−1>ai<ai+1)
【题目分析】
显然是dp,但是没有见过,不会优化。
【解题思路】
首先有一个很显然的dp方法,我们令fi,j,kfi,j,k表示只考虑a[1..i]和b[1..j]a[1..i]和b[1..j],
选择的两个子序列结尾分别是ai和bjai和bj,上升下降状态为kk的方案数。
那么我们有fi,j,k=∑fx,y,1−kfi,j,k=∑fx,y,1−k,其中x<i,y<jx<i,y<j。暴力转移的复杂度是O(kn2m2)O(kn2m2)的,显然不能接受。
我们可以考虑将决策点转移的方案数先dp掉,转移后面我们就可以用O(1)O(1)进行转移。
那么令gi,y,kgi,y,k表示从fx,y,kfx,y,k作为决策点出发,当前要更新的是ii的方案数,
hi,j,khi,j,k表示从fx,y,kfx,y,k作为决策点出发,已经经历了gg的枚举,当前更新的是jj的方案数。
转移的话则是要么更新,要么将i或ji或j枚举到i+1以及j+1i+1以及j+1。
因为每次只有一个变量在动,所以另一个变量可以表示上一个位置的值,可以表示上一个位置的值,方便判断上升还是下降。
这样做的时间复杂度就可以优化到O(knm)O(knm)
【参考代码】
原题地址
给定一个序列aa,现在将序列分成kk段,每一段的代价是这个区间逆序对的个数,问分割后的最小代价。
【题目分析】
显然是个决策单调性dp,我就不平行四边形优化了,直接分治好了。
【解题思路】
首先显然对于连续一段排序的代价就是这段逆序对的个数,然后我们可以dp,设fi,j表示将fi,j表示将[1,i]分成分成j$个连续段的最小代价即可。
这个dp显然又满足决策单调性,那么具有决策单调性的dp,可以直接平行四边形优化来做,当然我们很常见的还是分治求解。
用BIT维护一下区间逆序对个数即可。
你还可以选择用可持久化分块来达到更优的时间复杂度
【参考代码】
【总结】
关于决策点的dp真的是有很多玄学的优化姿势qwq。
还有下面的原题地址是没有题面的,题面在这里。
BZOJ5124波浪序列
【题目】原题地址
给定两个XX维向量序列a[1,n],b[1,m]a[1,n],b[1,m],求有多少个序列f,gf,g满足1≤f1<f2<...<fk≤n,1≤g1<g2<...<gk≤m1≤f1<f2<...<fk≤n,1≤g1<g2<...<gk≤m且afi=bfi,[af1,af2,...,afk]afi=bfi,[af1,af2,...,afk]是波浪的(波浪指对于每个非两端ii满足ai−1<ai>ai+1ai−1<ai>ai+1或ai−1>ai<ai+1ai−1>ai<ai+1)
【题目分析】
显然是dp,但是没有见过,不会优化。
【解题思路】
首先有一个很显然的dp方法,我们令fi,j,kfi,j,k表示只考虑a[1..i]和b[1..j]a[1..i]和b[1..j],
选择的两个子序列结尾分别是ai和bjai和bj,上升下降状态为kk的方案数。
那么我们有fi,j,k=∑fx,y,1−kfi,j,k=∑fx,y,1−k,其中x<i,y<jx<i,y<j。暴力转移的复杂度是O(kn2m2)O(kn2m2)的,显然不能接受。
我们可以考虑将决策点转移的方案数先dp掉,转移后面我们就可以用O(1)O(1)进行转移。
那么令gi,y,kgi,y,k表示从fx,y,kfx,y,k作为决策点出发,当前要更新的是ii的方案数,
hi,j,khi,j,k表示从fx,y,kfx,y,k作为决策点出发,已经经历了gg的枚举,当前更新的是jj的方案数。
转移的话则是要么更新,要么将i或ji或j枚举到i+1以及j+1i+1以及j+1。
因为每次只有一个变量在动,所以另一个变量可以表示上一个位置的值,可以表示上一个位置的值,方便判断上升还是下降。
这样做的时间复杂度就可以优化到O(knm)O(knm)
【参考代码】
#include<bits/stdc++.h> using namespace std; const int N=2005; const int M=7; const int mod=998244353; int n,m,ks,ans; int a [M],b [M],f [2],g [2]; bool equal(int *x,int *y) { for(int i=1;i<=ks;++i) if(x[i]^y[i]) return 0; return 1; } bool bigger(int *x,int *y) { for(int i=1;i<=ks;++i) if(x[i]<=y[i]) return 0; return 1; } bool smaller(int *x,int *y) { for(int i=1;i<=ks;++i) if(x[i]>=y[i]) return 0; return 1; } int main() { freopen("BZOJ5124.in","r",stdin); freopen("BZOJ5124.out","w",stdout); scanf("%d%d",&ks,&n); for(int i=1;i<=n;++i) for(int j=1;j<=ks;++j) scanf("%d",&a[i][j]); scanf("%d",&m); for(int i=1;i<=m;++i) for(int j=1;j<=ks;++j) scanf("%d",&b[i][j]); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) for(int k=0;k<2;++k) { if(equal(a[i],b[j])) { int t=g[i][j][k^1]; if(!k) (t+=1)%=mod; (ans+=t)%=mod; (f[i+1][j][k]+=t)%=mod; } if(f[i][j][k]) { (f[i+1][j][k]+=f[i][j][k])%=mod; if(!k) { if(bigger(a[i],b[j])) (g[i][j+1][k]+=f[i][j][k])%=mod; } else { if(smaller(a[i],b[j])) (g[i][j+1][k]+=f[i][j][k])%=mod; } } if(g[i][j][k]) (g[i][j+1][k]+=g[i][j][k])%=mod; } printf("%d\n",ans); return 0; }
BZOJ5125小Q的书架
【题目】原题地址
给定一个序列aa,现在将序列分成kk段,每一段的代价是这个区间逆序对的个数,问分割后的最小代价。
【题目分析】
显然是个决策单调性dp,我就不平行四边形优化了,直接分治好了。
【解题思路】
首先显然对于连续一段排序的代价就是这段逆序对的个数,然后我们可以dp,设fi,j表示将fi,j表示将[1,i]分成分成j$个连续段的最小代价即可。
这个dp显然又满足决策单调性,那么具有决策单调性的dp,可以直接平行四边形优化来做,当然我们很常见的还是分治求解。
用BIT维护一下区间逆序对个数即可。
你还可以选择用可持久化分块来达到更优的时间复杂度
【参考代码】
#include<bits/stdc++.h> #define lowbit(x) (x&(-x)) using namespace std; const int N=4e4+10; int n,m,L,R,now; int a ,tr ,f ,g ; inline void _reset() { memcpy(g,f,sizeof(g)); memset(f,0x3f,sizeof(f)); memset(tr,0,sizeof(tr)); L=1;R=now=0; } inline int query(int x) { int ret=0; for(;x;x-=lowbit(x)) ret+=tr[x]; return ret; } inline void update(int x,int v) { for(;x<=n;x+=lowbit(x)) tr[x]+=v; } inline void change(int l,int r) { while(R<r) now+=(R-L+1-query(a[R+1])),update(a[++R],1); while(L<l) now-=query(a[L]-1),update(a[L++],-1); while(L>l) now+=query(a[L-1]-1),update(a[--L],1); while(R>r) now-=(R-L+1-query(a[R])),update(a[R--],-1); } inline void solve(int l,int r,int dl,int dr) { int mid=(l+r)>>1,dm=dl; for(int i=dl;i<=min(dr,mid-1);++i) { change(i+1,mid); int t=g[i]+now; if(t<f[mid]) f[mid]=t,dm=i; } if(l<mid) solve(l,mid-1,dl,dm); if(r>mid) solve(mid+1,r,dm,dr); } int main() { freopen("BZOJ5125.in","r",stdin); freopen("BZOJ5125.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); update(a[i],1); f[i]=f[i-1]+i-query(a[i]); } for(int i=2;i<=m;++i) { _reset(); solve(i,n,i-1,n); } printf("%d\n",f ); return 0; }
【总结】
关于决策点的dp真的是有很多玄学的优化姿势qwq。
相关文章推荐
- 【bzoj5125】[Lydsy12月赛]小Q的书架 dp优化
- BZOJ 5128([Lydsy12月赛]寻找母串-区间dp)
- poj 1037 DP 求波浪序列中按字典序排列的第k个序列
- BZOJ 1563: [NOI2009]诗人小G 决策单调性DP
- BZOJ 5072 [Lydsy 十月月赛] 树DP 解题报告
- bzoj 3675 [Apio2014]序列分割(斜率DP)
- [BZOJ 2216][Poi2011]Lightning Conductor:DP决策单调性
- bzoj4709 -- 决策单调性优化DP
- BZOJ3675 [Apio2014]序列分割 【斜率优化dp】
- bzoj1563: [NOI2009]诗人小G【决策单调性优化dp】
- BZOJ 5123([Lydsy12月赛]线段树的匹配-记忆化搜索)
- BZOJ 1046 [HAOI2007]上升序列 DP
- bzoj 1046 : [HAOI2007]上升序列 dp
- BZOJ2216 [Poi2011]Lightning Conductor 【决策单调性dp】
- BZOJ3675 [Apio2014]序列分割 【斜率优化dp】
- bzoj 1049: [HAOI2006]数字序列(DP+DP)
- bzoj 1046: [HAOI2007]上升序列 (DP)
- [BZOJ3675][Apio2014]序列分割(斜率优化dp)
- 【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索
- BZOJ 1046: [HAOI2007]上升序列 LIS -dp