2017多校训练赛第四场 HDU 6078 Wavel Sequence(dp+优化)

来源:互联网 发布:php开源电子商务系统 编辑:程序博客网 时间:2024/06/07 05:36

Wavel Sequence

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 758    Accepted Submission(s): 394

Problem Description

Have you ever seen the wave? It's a wonderful view of nature. Little Q is attracted to such wonderful thing, he even likes everything that looks like wave. Formally, he defines a sequencea1,a2,...,an as ''wavel'' if and only if a1<a2>a3<a4>a5<a6...



Picture from Wikimedia Commons
Now given two sequences a1,a2,...,an and b1,b2,...,bm, Little Q wants to find two sequences f1,f2,...,fk(1fin,fi<fi+1) and g1,g2,...,gk(1gim,gi<gi+1), where afi=bgi always holds and sequence af1,af2,...,afk is ''wavel''.
Moreover, Little Q is wondering how many such two sequences f and g he can find. Please write a program to help him figure out the answer.

Input

The first line of the input contains an integer T(1T15), denoting the number of test cases.
In each test case, there are 2 integers n,m(1n,m2000) in the first line, denoting the length of a and b.
In the next line, there are n integers a1,a2,...,an(1ai2000), denoting the sequence a.
Then in the next line, there are m integers b1,b2,...,bm(1bi2000), denoting the sequence b.

Output

For each test case, print a single line containing an integer, denoting the answer. Since the answer may be very large, please print the answer modulo998244353.

Sample Input

13 51 5 34 1 1 5 3

Sample Output

10
Hint
(1)f=(1),g=(2).(2)f=(1),g=(3).(3)f=(2),g=(4).(4)f=(3),g=(5).(5)f=(1,2),g=(2,4).(6)f=(1,2),g=(3,4).(7)f=(1,3),g=(2,5).(8)f=(1,3),g=(3,5).(9)f=(1,2,3),g=(2,4,5).(10)f=(1,2,3),g=(3,4,5).

Source

2017 Multi-University Training Contest - Team 4



        第四场多校,到现在才补……

        又是类似最长公共子串类型的dp。大致题意是给你两个序列a和b,然后问你能够选出多少个f和g映射,使得a[fi]==b[fi],而且a[f1]、a[f2]、a[f3]……a[fn]是波浪序列。

        很容易想到的一个递推是dp[i][j][t]=Σdp[x][y][t^1](a[i]==b[j]且a[x]==b[y],同时满足波浪性),其中dp[i][j][t]表示序列a取到第i位,序列b取到第j位,而且当前是波峰(t==1)或者波谷(t==0)时的方案数。很显然,这个O(N^4)的复杂度是不能接受的。所以说要对决策的枚举或者转移进行一定的优化。

        由于转移的条件是a[i]==b[j],然后如果是波峰那么要从波谷转移,则对应末位要小于当前位,那么最后相当于是所有的末位小于当前位置的波谷的方案数之和。既然如此,我们能否通过一种方式快速知道这个合法方案数之和呢?答案是肯定的。我们观察到i是递增的所以不会产生影响,我们弄一个二维树状数组s[j][num][t]表示第二个串判断到j时末尾小于/大于num的方案数之和。这样就有转移方程dp[i][j][t]=getsum(j,b[j],t^1)。然后每次对应的更新方案数,时间复杂度就降到了O(N^2log2N),已经可以AC此题。

        但是这并不是我们想要的解法。我们之所以用到了树状数组,是因为我们还要考虑末位数字num的大小对结果的影响。但是实际上我们发现这个是多余的,因为真正能够转移的是a[i]==b[j]的情况,然而每次j从1~m循环一次,i才移动一次,以为着我们如果以a[i]作为判断依据可以不看很多细节。为了统计我么们一样设置一个数组s[i][j][t]表示处理到串的第i位和第j位时候的波峰/波谷数量,同时再记录cnt0和cnt1表示当前取到j时的波峰和波谷方案数。每次我们判断,如果a[i]>b[j],那么b[j]可以做后面潜在的波谷,于是cnt0加上dp[i-1][j][0];反之则b[j]可以做潜在的波峰,cnt1加上dp[i-1][j][1];当相等的时候,把cnt0和cnt1转移到相应状态。处理完当前决策i时,在相应更新s的数值。

        总的来说,这样一个优化,巧妙的利用了数组的统计功能,同时每次利用a[i]作为比较确定波峰波谷的方案数,更是节省了判断末位大小的时间。总的复杂度降到了O(N^2)然后由于状态只与前一状态有关,所以空间复杂度也是能够降到O(N)的,但是为了表示清楚,我还是没有这么写。具体见代码:

#include<bits/stdc++.h>#define mod 998244353#define N 2010using namespace std;int dp[N][N][2],s[N][N][2],a[N],b[N];int n,m,cnt0,cnt1;long long ans;int main(){    int T_T,T=0;    cin>>T_T;    while(T_T--)    {        ans=0;        scanf("%d%d",&n,&m);        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        for(int j=1;j<=m;j++)            scanf("%d",&b[j]);        for(int i=1;i<=n;i++)        {            cnt0=0; cnt1=1;            for(int j=1;j<=m;j++)            {                dp[i][j][0]=dp[i][j][1]=0;                if (a[i]==b[j])                {                    dp[i][j][1]=cnt0;                    dp[i][j][0]=cnt1;                    ans=(ans+cnt0+cnt1)%mod;                } else if (a[i]>b[j]) cnt0=(cnt0+s[i-1][j][0])%mod;                                 else cnt1=(cnt1+s[i-1][j][1])%mod;                s[i][j][0]=(s[i-1][j][0]+dp[i][j][0])%mod;                s[i][j][1]=(s[i-1][j][1]+dp[i][j][1])%mod;            }        }        printf("%I64d\n",ans%mod);    }    return 0;}