给出一个有n(<=2000)个数字的序列 a(ai <=2000) 再给出一个有m(m<=2000)个
数字的序列 b(bi<=2000) ,定义波浪序列为:x1<x2>x3<x4……(注意第一次必须
是上升,不能是下降,也就是说第一项必须是波谷,这也可以让我们明白,相对于
一个数来说,不管大小,它都可以作为 波谷的,因为下一个数一定要大于它)。现
在要求从a中找到序列 f:f1,f2,……fk。相对应的在b中找序列g:g1,g2,……
gk。(k>=1)使得每个a_fi =b_gi,同时满足a_f1,a_f2,a_f3……a_fk为波浪序列。求
不同的fg映射有多少种选取方式。
a,b中分别从前向后选取k个数字。然后相对应的 a 中选择的每个位置的数字要和 b
中选择的对应位次的数字相同。(当然如果a数组出现过x,而b没有出现过x,显然x
不可能被选取),而 f 、g 则是相对应的下标。要满足选取出来的这个数字序列是一
个波浪序列。显然波浪序列中的数字分成两种:波峰和波谷。
总体来说,这个题就是a、b数组之间的匹配问题,同时满足是一个波浪序列。分析: 用一个二维数组dp来存储b数组中每个数字作为波峰和波谷的两种情况:
dp[ ][0]用来表示当前数字为波谷时的情况
dp[ ][1]用来表示当前数字为波峰时的情况
每轮查找都不断更新dp的值
再用一个二维数组sum来存储b数组中每个数字作为波峰和波谷的次数,它的作用
其实是为在b数组中如果遇 到下一个和a[i]相等的数时做统计。
用sum[][0]来存储该数字在波谷时的总的情况数
sum[ ][1]用来存储该数字在波峰时的总的情况数
其实sum的作用是为后面的查询做统计(这个代码是用b数组作统计的)
下面我们来看代码实现吧。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const LL MOD = 998244353; const int N = 2e3+5; int b[N], a[N]; LL sum[N][2], dp[N][2];///sum存储b数组中每位数字两种情况下的总值; int main() { int T, n, m; scanf("%d", &T); while(T--) { memset(sum,0,sizeof(sum)); memset(dp,0,sizeof(dp)); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= m; i++) scanf("%d", &b[i]); LL ans = 0;///ans是计算b中所有数字能当波峰和波谷的所有情况,也就是最终的答案 for(int i = 1; i <= n; i++)///以a数组当前查询的点作为波浪结束点 { LL cnt0 = 1, cnt1 = 0;///cnt0表示波谷位置,cnt1表示波峰位置 for(int j = 1; j <= m; j++) { if(a[i] == b[j])///如果在b数组中查询到有相等的,则说明该点是波浪的结束点, ///开始统计它作为波峰和波谷的情况 { dp[j][0] = cnt0;///dp[][0]表示b[j]在波谷的情况 dp[j][1] = cnt1;///dp[][1]表示b[j]在波峰的情况 ans = (ans+cnt1+cnt0)%MOD;///ans表示计算两种情况的总合 } else if(b[j] < a[i])///如果b[j]<a[i],说明b[j]的值可以做a[i]的值得波谷,换句话说, ///当在b数组中找到与a[i]值相同的数时,可以统计它相对作为波峰的情况 (cnt1 += sum[j][0]) %= MOD; else///同上理解 (cnt0 += sum[j][1]) %= MOD; } for(int j = 1; j <= m; j++) { if(b[j] == a[i]) { (sum[j][0] += dp[j][0]) %= MOD; (sum[j][1] += dp[j][1]) %= MOD; } } } printf("%lld\n", ans); } return 0; }