xtu-1269 Similar Subsequence(dp+树状数组)

Similar Subsequence

Accepted : 17 Submit : 73
Time Limit : 10000 MS Memory Limit : 135536 KB

Similar Subsequence

For given sequence A=(a 1 ,a 2 ,,a n )  , a sequence S=(s 1 ,s 2 ,,s n )  has shape A  if and only if:

  • s i =min{s i ,s i+1 ,,s n }  for all a i =0  ;
  • s i =max{s i ,s i+1 ,,s n }  for all a i =1  .

Given sequence B=(b 1 ,b 2 ,,b m )  , Bobo would like to know the number of subsequences of length n  which have shape A  modulo (10 9 +7)  .

Input

The input contains zero or more test cases and is terminated by end-of-file. For each test case:

The first line contains two integers n  and m  .

The second line contains n  integers a 1 ,a 2 ,,a n   .

The thrid line contains m  integers b 1 ,b 2 ,,b m   .

  • The number of test cases does not exceed 10  .
  • 1n20 
  • 1m500 
  • 0a i 1 
  • 1b i m 
  • b 1 ,b 2 ,,b m   are distinct.

Output

For each case, output an integer which denotes the number of subsequences modulo (10 9 +7)  .

Sample Input

2 3
0 0
1 2 3
3 5
1 0 1
4 1 3 2 5

Sample Output

3
2

Note

For the first sample, all three subsequences of length 2  are of shape A  .


dp[i][j][x][y]表示a串中第i个元素,b串中的第j个元素,最小值为x,最大值为y时的方案数
dp[i][j][x][y]=∑dp[i-1][j'][x'][y'],[x',y']∈[x,y]
由于x,y中一定有一项等于b[j]因此可以省略一维,状态数为n*m^2

dp[i][j][k]中,
a[i]==0时,k为第i~n个被匹配的元素中最大值
a[i]==1时,k为第i~n个被匹配的元素中最小值

状态转移时用树状数组来维护:t[i][k]中i和k分别对应于dp[i][j][k]中的i和k
如果a[i]==0,sum[x]表示上下界为[1~x,k]的方案数之和
如果a[i]==1,sum[x]表示上下界为[k,1~x]的方案数之和

#include<bits/stdc++.h>
#define fuck(x) cout<<'['<<x<<']';
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int mod = 1e9 + 7;
const int MX = 500 + 5;
int a[25], b[MX], dp[25][MX][MX];
int n, m;
void add(int &x, int y) {
    x += y;
    if(x >= mod) x -= mod;
    if(x < 0) x += mod;
}
struct Tree {
    int sum[MX];
    int lowbit(int x) {
        return x & (-x);
    }
    void update(int x, int v) {
        while(x) {
            add(sum[x], v);
            x -= lowbit(x);
        }
    }
    int query(int x) {
        if(x == 0) return 0;
        int ret = 0;
        while(x <= m) {
            add(ret, sum[x]);
            x += lowbit(x);
        }
        return ret;
    }
    void clear() {
        memset(sum, 0, sizeof(sum));
    }
} t[25][MX];
int main() {
    //reopen("in.txt", "r", stdin);
    while(~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]);
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                t[i][j].clear();
        /*
        初始化是个难点,如果a[1]==1,那么要设下界为最小值1
        如果a[1]==0,那么要设下界为最大值m
        if(a[1]==1) t[1][1].update(m,1);
        else t[1][m].update(m,1);
        */
        for(int i=1;i<=m;i++) dp[1][i][a[1]==1?1:m]=1;
        for(int i = 2; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                for(int k = 1; k <= m; k++) {
                    dp[i][j][k] = t[i][k].query(b[j]);
                }
                for(int k = 1; k <= m; k++) {
                    if(dp[i - 1][j][k] == 0) continue;
                    //匹配到第i-1个数位置,上下边界为[L,R]
                    int L = b[j], R = k;
                    if(a[i - 1] == 1) swap(L, R);
                    if(L > R) continue;
                    if(a[i] == 0) { //如果a[i]==0,则下边界就是b[j],因此要枚举上边界R
                        t[i][R].update(R, dp[i - 1][j][k]);
                        t[i][R].update(L - 1, -dp[i - 1][j][k]);
                    } 
                    else {   //如果a[i]==1,则下边界就是b[j],因此要枚举下边界L
                        t[i][L].update(R, dp[i - 1][j][k]);
                        t[i][L].update(L - 1, -dp[i - 1][j][k]);
                    }
                }
            }
        }
        int ans = 0;
        for(int j = 1; j <= m; j++)
            for(int k = 1; k <= m; k++)
                add(ans , dp[n][j][k]);
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值