wmq的队伍 BIT优化dp

http://oj.xjtuacm.com/problem/14/

wmq的队伍

发布时间: 2017年4月9日 17:06   最后更新: 2017年4月9日 17:07   时间限制: 2000ms   内存限制: 512M

交大上课需要打卡,于是在上课前的几分钟打卡机前往往会排起长队。

平时早睡早起早早打卡的wmq昨晚失眠,今天起晚了,于是他也加入了打卡队伍中。

这个时候,wmq发现了神奇的现象,打卡队伍可以按人们的身高看成一个队列,左边是队头,右边是队尾。

对于队列a1...an,wmq想知道其中存在多少的有序k元组l1...lk

使得1l1<l2<...<lkn,并且有al1>al2>...>alk

输入有多组数据

第一行是一个正整数T1T15,代表数据组数

每组数据第一行是两个正整数nk1n21041kmin(n,100)

n代表队列的人数,k 的含义见题面

接下来一行有n个正整数,代表1n的一个排列,表示队伍的身高情况

对于每组数据,输出一个整数,代表有序k元组的个数

考虑到数字可能很大,将答案对109+7取模之后输出

 复制
3
2 2
1 2
2 2
2 1
22 3
1 2 3 4 5 16 6 7 8 9 10 19 11 12 14 15 17 18 21 22 20 13
0
1
8

这题是一眼看破的题了,设dp[i][k]表示以第i个数字结尾的时候,下降子序列长度为k时的种类数。

那么dp[i][k] = sigma dp[res][k - 1],a[res] > a[i], 1 <= res <= i - 1

那么答案就是 sigma dp[i][k],    1 <= i  <= n

然后发现这样会超时,就马上否认了这个做法。于是做了3小时。

其中有一种比较有趣的想法是考虑每一个数字贡献,以

4、3、2、1,k = 3为例子。算出每一个位置的逆序对数,那么对于每一个点,如果它的逆序对 + 1 >= k的,就可以作为贡献

也就是,比如2的逆序对有2对,那么加上2就形成一个长度为3的下降子序列,这个时候有C(2, 2) = 1种,意思就是不包括2的话,我还要选出k - 1 = 2个数字,形成长度是3的下降子序列。那么1的逆序对有3种,贡献是C(3, 2) = 3种,最后ans = 4是对的。

这个方法思路很清晰,也很好理解,是bit + 组合数学,意思就是在前面m个数字选出k - 1个,形成长度是k的子序列。

但是这里忽略了16、19、11,k = 3这样的情况,因为虽然11的逆序对是2,能对答案造成贡献,但是这里11的逆序对互相不能形成逆序对。所以这样的做法是不行的。

然后设了很多dp,发现还是这个能做。暴力了一下,这个dp是正确的。

然后就是怎么优化了。

希望复杂度是O(nk)

那么我就需要快速知道,前i个数,有多少个比a[i]大的数字,而且它们产生的下降子序列长度是k。的总和

bit数组c[k]维护的是下降子序列长度是k时的数字情况。

具体看看代码。以后写出一个合法的dp,超时的,优化下,想一想有没什么数据结构或者某一维是并不需要关心的,比如浙大的校赛那题。dp就是带技巧的暴力,

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
const int maxn = 20000 + 20;
int dp[maxn][100 + 20];
int a[maxn];
int c[100 + 2][maxn];
int n, k;
const int MOD = 1e9 + 7;
void add(int &x, int val) {
    x += val;
    if (x >= MOD) x -= MOD;
}
int lowbit(int x) {
    return x & (-x);
}
void upDate(int pos, int val, int which) {
    while (pos) {
        add(c[which][pos], val);
        pos -= lowbit(pos);
    }
}
int ask(int pos, int which) {
    int ans = 0;
    while (pos <= n) {
        add(ans, c[which][pos]);
        pos += lowbit(pos);
    }
    return ans;
}
void work() {
    scanf("%d%d", &n, &k);
    memset(c, 0, sizeof c);
    memset(dp, 0, sizeof dp);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n; ++i) {
        dp[i][1] = 1;
        for (int j = 2; j <= k; ++j) {
            add(dp[i][j], ask(a[i] + 1, j - 1));
        }
        for (int j = 1; j <= k; ++j) {
            upDate(a[i], dp[i][j], j);
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        add(ans, dp[i][k]);
    }
    printf("%d
", ans);
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
//    cout << comb[7][2] << endl;
//    cout << comb[4][2] << endl;
    int t;
    scanf("%d", &t);
    while (t--) work();
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/liuweimingcprogram/p/6706224.html