Xiangtan Invitation Contest 2017 - J Similar Subsequence(DP)

本文介绍了一种算法,用于计算给定序列B中具有特定形状A的子序列数量。通过定义状态f[i,j,k]来表示当前匹配的位置及最大或最小值的状态,使用树状数组进行优化,最终实现高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

For given sequence A = (a1, a2, . . . , an), a sequence S = (s1, s2, . . . , sn) has shape A 

if and only if:• si = min{si, si+1, . . . , sn} for all ai = 0;• si = max{si, si+1, . . . , sn} for all ai = 1.

Given sequence B = (b1, b2, . . . , bm), Bobo would like to know the number of subsequences of length n whichhave shape A modulo (109 + 7).


分析:f[i,j,x,y]表示当前匹配到a的第i个位置,b的第j个位置,最大值为x,最小值为y的方案数,那么下一个决策就是f[i+1,j',x',y'] 其中j' > j且b[j] ∈[x,y],注意到x 和 y中一定有一个等于b[j],这样可以把状态精简为f[i,j,k],k表示最大值或最小值,此时如果暴力转移复杂度是n*m^3,可以用树状数组优化到n*m^2*log(m).


#include <bits/stdc++.h>
#define N 100005
#define MOD 1000000007
using namespace std;
typedef long long ll;
int n,m,a[25],b[505],f[25][505][505],p[25][505][505];
bool could(int i,int j,int k)
{
    if(a[i]) return b[j] >= k;
    else return b[j] <= k;
}
void Insert(int i,int j,int k,int x)
{
    while(k <= m)
    {
        p[i][j][k] += x;
        p[i][j][k] %= MOD;
        k += k & (-k);
    }
}
int Find(int i,int j,int k)
{
    int ans = 0;
    while(k)
    {
        ans = ans + p[i][j][k];
        ans %= MOD;
        k -= k & (-k);
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m) != EOF)
    {
        memset(p,0,sizeof(p));
        memset(f,0,sizeof(f));
        for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
        for(int i = 1;i <= m;i++) scanf("%d",&b[i]);
        for(int j = m;j;j--)
        {
            for(int i = n;i;i--)
             for(int k = 1;k <= m;k++)
              if(could(i,j,k))
              {
                  int l = min(k,b[j]),r = max(k,b[j]);
                  int c = a[i+1] ? l : r;
                  if(i == n) f[i][j][k] = 1;
                  else f[i][j][k] = (Find(i+1,c,r) - Find(i+1,c,l-1) + MOD) % MOD;
              }
            for(int i = n;i;i--)
             for(int k = 1;k <= m;k++)
              if(could(i,j,k)) Insert(i,k,b[j],f[i][j][k]);
        }
        int ans = 0;
        for(int j = 1;j <= m;j++)
        {
            if(a[1]) ans = (ans + f[1][j][1]) % MOD;
            else ans = (ans + f[1][j][m]) % MOD;
        }
        printf("%d\n", ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值