Consider a permutation a 1, a 2, …, an (all ai are different integers in range from 1 to n). Let us call k-inversion a sequence of numbers i 1, i 2, …, ik such that 1 ≤ i 1 < i 2 < … < ik ≤ n and ai 1 > ai2 > … > aik. Your task is to evaluate the number of different k-inversions in a given permutation.
Input
The first line of the input contains two integers n and k (1 ≤ n ≤ 20000, 2 ≤ k ≤ 10). The second line is filled with n numbers ai.
Output
Output a single number — the number of k-inversions in a given permutation. The number must be taken modulo 10 9.
Example
input | output |
---|---|
| |
| |
Solution:
我们定义数组dp[i][j]代表以a[i]结尾的长度为j的序列的情况数量,那么可以得到状态转移方程:
dp[i][j] = ,其中k代表的是下标为1~i - 1中a[k] > a[i]的数的下标。有了状态转移方程问题就解决了,但是不能暴力跑,时间不允许,我们要用到大于a[i] 的dp值的和,可以用线段树来优化,logn的复杂度还是可以接受的。每次只要查询一下a[i]+1~n的区间和就好了(注意判断a[i] + 1 和 n 的大小关系),值得一提的是我开了10棵线段树,分别用来存放 j 从1~k的值(用树状数组做的话代码量更短,可惜我没学呢。。)。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 2 * 1e4 + 100;
const int mod = 1e9;
ll dp[maxn][20];
int a[maxn], ma[maxn];
int n, k;
ll tree[maxn << 2][12];
void push_up(int rt, int pos) {
tree[rt][pos] = (tree[rt << 1][pos] +tree[rt << 1 | 1][pos]) % mod;
}
void update(int pos, ll val, int floor, int l, int r, int rt) //很常规的单点更新
{
if(l == r && l == pos) {
tree[rt][floor] = val;
return ;
}
int m = (l + r) >> 1;
if(pos <= m)
update(pos, val, floor, l, m, rt << 1);
else
update(pos, val, floor, m + 1, r, rt << 1 | 1);
push_up(rt, floor);
}
ll query(int L, int R, int floor, int l, int r, int rt) //很常规的区间查询
{
if(L > R) //顶多也就多了个判断是否合法
return 0;
if(l >= L && r <= R) {
return tree[rt][floor];
}
ll ans = 0;
int m = (l + r) >> 1;
if(m >= L)
ans = (ans + query(L, R, floor, l, m, rt << 1)) % mod;
if(m < R)
ans = (ans + query(L, R, floor, m + 1, r, rt << 1 | 1)) % mod;
return ans;
}
int main()
{
//freopen("in.txt", "r", stdin);
cin >> n >> k;
for(int i = 1; i <= n; ++ i)
{
cin >> a[i];
}
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; ++ i) //1~n放外层循环
{
for(int j = 1; j <= k; ++ j) //1~k放内层循环
{
if(j == 1)
dp[i][j] = 1; //不能放在外面一次性初始化为1,我们每次需要的是在a[i]前面并且比a[i]大的数的dp值,而不是单单的比a[i]大的数的dp值
//我们更新dp[i][j]时需要的是j - 1时候的状态,如果一次性初始化为1的话,我们的查询结果就是大于a[i]的所有数的dp值了。
else
dp[i][j] = query(a[i] + 1, n, j - 1, 1, n, 1);
update(a[i], dp[i][j], j, 1, n, 1);
}
}
ll ans = 0;
for(int i = 1; i <= n; ++ i) //把以每个数为结尾的情况全部加起来就得到了最终的答案
ans = (ans + dp[i][k]) % mod;
cout << ans << endl;
return 0;
}