bzoj 5124: [Lydsy1712月赛]波浪序列 关于决策点dp的优化

题意

给定两个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−1ai+1ai−1ai+1或ai−1>ai<ai+1ai−1>ai<ai+1)

【解题思路】
首先有一个很显然的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。
因为每次只有一个变量在动,所以另一个变量可以表示上一个位置的值,可以表示上一个位置的值,方便判断上升还是下降。

思路和方法

因为是枚举两个序列匹配的问题,每个合法状态都是这个一对(i,j)匹配。
可以从决策点先枚举其中一维向下走,再枚举另一维,将dp的转移变成O(1)。这个思路非常巧妙

写题的注意

没有模拟样例,开始以为子序列的长度为K。

推式子没有理清转移的先后关系,对于dp的转移的写法过于生疏。

这样的dp一定要写一遍才能有深刻的印象!不能浮躁和逃避,必须写!当然,这道题想题花了50min,写题+调花了50min,非常不应该。要想清楚再写,把写题时间缩短,能够一次AC可以节省至少半个小时!

#include<bits/stdc++.h>
using namespace std;
#define maxn 2020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define inf 0x3f3f3f3f
typedef long long ll;
typedef pair<int,int> pr;

const ll mod = 998244353;
int f[maxn][maxn][2],g[maxn][maxn][2],h[maxn][maxn][2];
int a[maxn][6],b[maxn][6],tag[maxn][maxn],n,k,m;

inline int cmp(int a[],int b[]){
	int t = -1;
	rep(i,1,k){
	   	if ( t == -1 ){
			if ( a[i] == b[i] ) t = 1;
			else if ( a[i] > b[i] ) t = 2;
			else t = 3;
		}
		else{
			int c;
			if ( a[i] == b[i] ) c = 1;
			else if ( a[i] > b[i] ) c = 2;
			else c = 3;
			if ( c != t ) return -1;
		}
	}
	return t;
}
//cmp(a,b) = 1 : a == b
//cmp(a,b) = 2 : a > b
//cmp(a,b) = 3 : a < b
//cmp(a,b) = -1 : 无法比较 
inline void up(int &x,int y){ 
	x += y;
	if ( x >= mod ) x -= mod;
}
int main(){
	scanf("%d %d",&k,&n);
	rep(i,1,n) rep(j,1,k) scanf("%d",&a[i][j]);
	scanf("%d",&m);
	rep(i,1,m) rep(j,1,k) scanf("%d",&b[i][j]);
	rep(i,1,n) rep(j,1,m){
		tag[i][j] = cmp(a[i],b[j]);
		if ( tag[i][j] == 1 ) f[i][j][1] = 1;
	}
	int t = 1;
	rep(i,1,n){
		rep(j,1,m){
			//g[i][j]维护的是固定j,枚举i作为下一个选的向量。
			//当ai,bj关系合法时,转移给h[i][j]
			//当h[i][j]中ai==bj时,可以作为一个合法的向量对,给f[i][j]
			
			if ( tag[i][j] == 1 ){ //注意转移顺序。 
				rep(t,0,1){	
					up(h[i][j][t],h[i][j - 1][t]);
					up(f[i][j][t ^ 1],h[i][j][t]);				
				}
				rep(t,0,1){
					up(g[i][j][t],(g[i - 1][j][t] + f[i][j][t]) % mod);
				}
			}
			else{
				rep(t,0,1){
					up(g[i][j][t],g[i - 1][j][t]);
					up(h[i][j][t],h[i][j - 1][t]);
					if ( t && tag[i][j] == 2 ) up(h[i][j][t],g[i][j][t]);
					else if ( !t && tag[i][j] == 3 ) up(h[i][j][t],g[i][j][t]);
				}
			}
		
		}
	}
	int ans = 0;
	rep(i,1,n) rep(j,1,m){
	   	up(ans,f[i][j][0]);
		up(ans,f[i][j][1]);
	//	cout<<i<<" "<<j<<" "<<f[i][j][0]<<" "<<f[i][j][1]<<endl;
	}
	cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_42484877/article/details/86183229
今日推荐