2020牛客多校#2 G - Greater and Greater 动态规划+bitset优化

题目链接:
https://ac.nowcoder.com/acm/contest/5667/G

  思路与官方题解相同,只是描述方式不同。

  设 d p i , j dp_{i,j} dpi,j表示子序列 [ A i , A i + m − j ] [A_i,A_{i+m-j}] [Ai,Ai+mj]与子序列 [ B j , B m ] [B_j,B_m] [Bj,Bm]是否匹配,即 ∀ k ∈ [ 0 , m − j ] , A i + k ≥ B j + k \forall k \in [0,m-j],A_{i+k}\ge B_{j+k} k[0,mj]Ai+kBj+k匹配则为 1 1 1,不匹配则为 0 0 0。可以想到答案就是 j = 1 j=1 j=1时子序列 [ A i , A i + m − 1 ] [A_i,A_{i+m-1}] [Ai,Ai+m1]与子序列 [ B 1 , B m ] [B_1,B_m] [B1,Bm]匹配的总数,即 a n s = ∑ i = 1 n − m + 1 d p i , 1 ans=\sum_{i=1}^{n-m+1}{dp_{i,1}} ans=i=1nm+1dpi,1

  考虑 d p dp dp的转移方程 d p i , j = d p i + 1 , j + 1 & ( A i ≥ B j ) dp_{i,j}=dp_{i+1,j+1}\&(A_i\ge B_j) dpi,j=dpi+1,j+1&(AiBj)即若想子序列 [ A i , A i + m − j ] [A_i,A_{i+m-j}] [Ai,Ai+mj]与子序列 [ B j , B m ] [B_j,B_m] [Bj,Bm]匹配,则需要子序列 [ A i + 1 , A i + m − j ] [A_{i+1},A_{i+m-j}] [Ai+1,Ai+mj]与子序列 [ B j + 1 , B m ] [B_{j+1},B_m] [Bj+1,Bm]匹配,且 A i ≥ B j A_i\ge B_j AiBj

  可以想到,这个转移的计算复杂度是 O ( n m ) O(nm) O(nm),约 6 e 9 6e9 6e9,超时,也超空间。

  由于所有 d p dp dp值都是0或1,可以采用bitset优化,这样复杂度就变为 O ( n m w ) O(\frac{nm}{w}) O(wnm) w = 32 或 64 w=32或64 w=3264,处于可以接受的范围。

  设 b i t d p i [ j ] = d p i , j bitdp_i[j]=dp_{i,j} bitdpi[j]=dpi,j,这样有关 j j j的操作,都可以用bitset优化。可以用右移将 j j j向前移动一位,使得 b i t d p i + 1 [ j + 1 ] bitdp_{i+1}[j+1] bitdpi+1[j+1]转移到 b i t d p i [ j ] bitdp_i[j] bitdpi[j]。对于 A i ≥ B j A_i\ge B_j AiBj的判断,可以预处理一个长度为 m m m的bitset表 S i S_i Si S i [ j ] = ( A i ≥ B j ) S_i[j]=(A_i\ge B_j) Si[j]=(AiBj),这样,转移式就优化为 b i t d p i = ( b i t d p i + 1 > > 1 ∣ ( 1 < < m ) ) & S i bitdp_i=(bitdp_{i+1}>>1|(1<<m))\&S_i bitdpi=(bitdpi+1>>1(1<<m))&Si
  计算 S i S_i Si时,把 m m m B j B_j Bj排序后,那么每个 A i A_i Ai只会插在这些 B j B_j Bj中间或两边,因此最多只有 m + 1 m+1 m+1 S i S_i Si(从全是 0 0 0的情况,到全是 1 1 1的情况)。第 j j j S i S_i Si,就是在前一种 S i S_i Si的基础上在第 j j j大的 B j B_j Bj位置上从 0 0 0变为 1 1 1。这样就可以在 O ( m 2 w ) O(\frac{m^2}{w}) O(wm2)的复杂度内预处理完 S i S_i Si

代码如下(不是我写的):

#include<bits/stdc++.h>

using namespace std;

const int MAXN=2e5+10,MAXM=4e4+10;
int n,m,ans,a[MAXN],b[MAXM],pos[MAXN];
bitset<MAXM> s[MAXM],cur,w;

bool cmp(int x,int y){return b[x]<b[y];}

int main()
{
    scanf("%d%d",&n,&m);w[m]=1;
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    for(int i=1;i<=m;i++) scanf("%d",b+i),pos[i]=i;
    sort(pos+1,pos+1+m,cmp);
    sort(b+1,b+1+m);
    for(int i=1;i<=m;i++) s[i]=s[i-1],s[i][pos[i]]=1;
    for(int i=n;i>=1;i--)
	{
        int d=upper_bound(b+1,b+1+m,a[i])-b-1;
        cur=(((cur>>1)|w)&s[d]);
        if(cur[1]) ans++;
    }
    printf("%d\n",ans);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值