多校4 HDU-6078 Wavel Sequence 前缀和 & 优化dp

原题链接:
HDU-6078
大意:
给出两个字符串 a[i] b[i] 从中分别取数,组成 c[i] c[i] 满足 c1<c2>c3<c4>c5...... 波浪形。
求有多少种取法。

思路:
按照 dp 的思路,每次取第 i j 个数的时候至于前一个数有关,而且分为两种状态:这次取波谷或是取波峰.

这里写图片描述

fi,j,1=x=1iy=1jfx,y,0

fi,j,0=x=1iy=1jfx,y,1

dp[i][j][0]=sum{ dp[k][l][1] | x<y,k<i,l<j} 其中x=a[i]=b[j],y=a[k]=a[j]
dp[i][j][1]=sum{ dp[k][l][0] | x>y,k<i,l<j} 其中x=a[i]=b[j],y=a[k]=a[j]

这个复杂度是 O(n^4)的 ,利用滚动数组优化掉一维.
现在,考虑 dp[j][2]
循环 for(i 1 to n) for( j 1 to m) 的含义表示 在前 i 个 ai 范围内,考虑 bj 的情况。

利用 sum[0] 和 sum[1] 储存

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mem(s,t) memset(s,t,sizeof(s))
#define D(v) cout<<#v<<" "<<v<<endl
#define inf 0x3f3f3f3f
//#define LOCAL
inline void read(int &x){
    x=0;char p=getchar();
    while(!(p<='9'&&p>='0'))p=getchar();
    while(p<='9'&&p>='0')x*=10,x+=p-48,p=getchar();
}
const int MAXN =2e3+10;
const int mod= 998244353;
int dp[MAXN][2],a[MAXN],b[MAXN],sum[2];
int main() {
#ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    int t;
    read(t);
    while(t--){
        int n,m;
        read(n);read(m);
        mem(a,0);mem(b,0);mem(dp,0);mem(sum,0);
        for(int i=1;i<=n;i++){
            read(a[i]);
        }
        for(int i=1;i<=m;i++){
            read(b[i]);
        }
        for(int i=1;i<=n;i++){
            sum[0]=sum[1]=0;
            for(int j=1;j<=m;j++){
                if(a[i]==b[j]){
                    dp[j][0]=(dp[j][0]+1)%mod;
                    dp[j][0]=(dp[j][0]+sum[1])%mod;
                    dp[j][1]=(dp[j][1]+sum[0])%mod;
                }
                if(a[i]>b[j]) sum[0]=(dp[j][0]+sum[0])%mod;
                if(a[i]<b[j]) sum[1]=(dp[j][1]+sum[1])%mod;
            }
        }
        ll ans=0;
        for(int j=1;j<=m;j++){
            ans=(ans+dp[j][0]+dp[j][1])%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

附:官方题解和标程
这里写图片描述

#include<cstdio>
const int N=2010,P=998244353;
int Case,n,m,i,j,k,t,a[N],b[N],g[N][N][2],h[N][N][2],ans;
inline void up(int&x,int y){x=x+y<P?x+y:x+y-P;}
int main(){
  scanf("%d",&Case);
  while(Case--){
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    for(i=1;i<=m;i++)scanf("%d",&b[i]);
    for(i=1;i<=n;i++)for(j=1;j<=m;j++)for(k=0;k<2;k++)g[i][j][k]=h[i][j][k]=0;
    for(ans=0,i=1;i<=n;i++)for(j=1;j<=m;j++)for(k=0;k<2;k++){
      if(a[i]==b[j]){
        t=h[i][j][k^1];
        if(!k)up(t,1);
        if(t){
          up(ans,t);
          up(g[i+1][j][k],t);
        }
      }
      if(g[i][j][k]){
        up(g[i+1][j][k],g[i][j][k]);
        if(!k){if(a[i]>b[j])up(h[i][j+1][k],g[i][j][k]);}
        else if(a[i]<b[j])up(h[i][j+1][k],g[i][j][k]);
      }
      if(h[i][j][k])up(h[i][j+1][k],h[i][j][k]);
    }
    printf("%d\n",ans);
  }
  return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值