逆序对数列[HAOI2009]

【题目描述】
对于一个数列\(a\),如果有\(i<j\)\(a_i>a_j\),那么我们称\(a_i\)\(a_j\)为一对逆序对数。若对于任意一个由\(1\sim n\)自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为\(k\)的这样自然数数列到底有多少个?

【输入格式】
第一行为两个整数\(n,k\)

【输出格式】
写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对\(10000\)求余数后的结果。

题解

挺水的吧。。。简单推一下DP方程

\(dp[i][j]\)表示\(1\sim i\)的全排列中逆序对数量为\(j\)的有多少个

边界:\(dp[2][0]=dp[2][1]=1\) 特别注意\(dp[1][0]=1\)

下面是转移方程

举个栗子吧:\(3,1,2\) 现在把\(4\)加进去 如果加在第\(k\)个数后面\((0\le k\le 3)\) 逆序对就会多\(3-k\)

那就完事啦 \(dp[i][j]=\sum\limits_{k=0}^{i-1}dp[i-1][j-k]\)

就比如推\(dp[4][3]\)\(4\)在排列第一位,且逆序对数量为\(3\)的方案数就是\(dp[3][0]\) 因为你把\(4\)放第一个就一定会多产生\(3\)个逆序对嘛 那原来\(3\)个数就只能是没有逆序对了

前缀和\(O(1)\)转移

\(dp\)数组第一维可以去掉然后离线做 虽然这个数据很水 不去掉也没事 但是有一年ACM/ICPC好像考了原题 但是卡空间

然后离线给询问从小到大排个序 边DP边存答案 时间复杂度\(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
	return x * f;
}

const ll mod = 1000000007;
int tot, now;
ll dp[5005], sum[5005], ans[5005];

struct question{
    int n, m, ind;
} q[5005];

inline bool cmp(question a, question b) {
    return a.n < b.n;
}

inline void DP() {
	dp[0] = dp[1] = 1;
	for (ll j = 0; j <= 5000; j++) {
		sum[j] = j == 0 ? 1 : 2;
	} 
    while (now <= tot && q[now].n == 2) {
        ans[q[now].ind] = dp[q[now].m];
        now++;
    }
	for (ll i = 3; i <= 5000; i++) {
        memset(dp, 0, sizeof(dp));
		for (ll j = 0; j <= min(i * (i - 1) / 2, 5000ll); j++) {
			if (j - i < 0) {
				dp[j] = sum[j];
			} else {
				dp[j] = (sum[j] - sum[j - i] + mod) % mod;
			}
		}
        memset(sum, 0, sizeof(sum));
		sum[0] = dp[0];
		for (ll j = 1; j <= 5000; j++) {
			sum[j] = (sum[j-1] + dp[j]) % mod;
		}
        while (now <= tot && q[now].n == i) {
            ans[q[now].ind] = dp[q[now].m];
            now++;
        }
	}	
}

int main() {
    tot = read();
	for (int i = 1, n, m; i <= tot; i++) {
		n = read(); m = read(); 
		q[i] = question{n, m, i};
	}
    sort(q + 1, q + tot + 1, cmp);
    now = 1; 
    while (now <= tot && q[now].n == 1) {
        ans[q[now].ind] = q[now].m == 0 ? 1 : 0;
        now++;
    }
    DP();
    for (int i = 1; i <= tot; i++) {
        printf("%lld\n", ans[i]);
    }
	return 0;
} 
posted @ 2020-03-12 12:39  AK_DREAM  阅读(178)  评论(0编辑  收藏  举报