天天看点

Codeforces 597C(树状数组 + dp)

@(K ACMer)

题意:

由1到n,共n个数构成的序列中长为k + 1的上升子序列有多少个?

分析:

注意到题中数字是1到n,且子序列最长只有11.我们可以这样定义状态: dp[i][j] 为长度为i的子序列中,末尾数以j结尾的个数.

那么容易有转移方程:

dp[i][j]=∑t=1...j−1dp[i−1][t]

显然这个复杂度是 O(k∗n2) 的,过大,注意到我们这里每次都是求的数组的前缀和,那不是用一个树状数组来维护就好了么?这样复杂度就变为了 O(k∗n∗logn)

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
typedef unsigned long long ll;
typedef vector<int> vi;
#define xx first
#define yy second
const int mod = int() + , INF = , maxn =  + ;
ll dp[][maxn];
int n, k, ans;

ll sum(int x, int i) {
    ll ret = ;
    while (i > ) {
        ret += dp[x][i];
        i -= i & -i;
    }
    return ret;
}

void add(int y, int i, ll x) {
    while (i <= n) {
        dp[y][i] += x;
        i += i & -i;
    }
    return;
}

int main(void) {
    scanf("%d%d", &n, &k);
    for (int i = ; i < n; i++) {
        int x;
        scanf("%d", &x);
        for (int i = k + ; i >= ; i--) {
            ll temp = (i -  == ) ?  :sum(i - , x - );
            add(i, x, temp);
        }
    }
    cout << sum(k + , n) << endl;
    return ;
}