【vijos】1757 逆序对(dp)

https://vijos.org/p/1757

有时候自己sb真的是不好说。。。

我竟然想了半天都没想到这个转移。

我是有多傻。。。。

我们设f[i][j]表示1~i的排列且逆序对恰好是j的方案数。

显然我们只需要将i放进i-1排列中就行了。

而且发现i始终大于i-1

那么就好做了,我们只要将所有i放到i-1序列的位置的方案全都加起来即可,即:

f[i][j]=sum{f[i-1][k], max{0, j-i+1}<=k<=j}

用前缀和搞搞就行了。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
#define printarr1(a, b) for1(_, 1, b) cout << a[_] << ‘\t‘; cout << endl
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }
inline const int max(const int &a, const int &b) { return a>b?a:b; }
inline const int min(const int &a, const int &b) { return a<b?a:b; }

const int N=1005, MD=10000;
int f[N], sum[N], n, k;

int main() {
	int cs=getint();
	while(cs--) {
		CC(f, 0); CC(sum, 0);
		f[0]=1;
		read(n); read(k);
		for1(i, 1, n) {
			for1(j, 1, k+1) sum[j]=sum[j-1]+f[j-1], sum[j]%=MD;
			for1(j, 0, k) f[j]=(sum[j+1]-sum[max(0, j-i+1)]+MD)%MD;
		}
		printf("%d\n", f[k]);
	}
	return 0;
}

  


描述

对于1-n的任意一个排列:a1,a2,a3...an,如果存在i<j,且ai>aj,则(i,j)称之为一对逆序对。

我们常常关心一个排列的逆序对的总数,因为它可以反映一个排列的有序程度。

现在小D想知道,在1-n的所有排列中,有多少排列的逆序对总数恰好为k。

格式

输入格式

第一行为正整数T,表示数据组数
接下来T行,每行两个正整数:n,k

输出格式

对于每个输入,输出一行表示恰好为k的排列的个数。由于数字可能较大,只需要输出mod10000的结果即可。

样例1

样例输入1[复制]

1
4 1

样例输出1[复制]

3

限制

每个测试点1s

提示

对于样例的解释,下面的排列满足条件:
1 2 4 3
1 3 2 4
2 1 3 4

对于30%的数据 n<=12;
对于100%的数据 n<=1000,k<=1000,T<=10;

时间: 2024-08-04 05:37:17

【vijos】1757 逆序对(dp)的相关文章

bzoj2431: [HAOI2009]逆序对数列(前缀和优化dp)

2431: [HAOI2009]逆序对数列 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2312  Solved: 1330[Submit][Status][Discuss] Description 对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的 数列,可以很容易求出有多少个逆序对数.那么逆序对数为k的这样自然数数列到底有多少个? Input 第一行为两个整数n,k. Ou

【bzoj2431】[HAOI2009]逆序对数列 dp

题目描述 对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的 数列,可以很容易求出有多少个逆序对数.那么逆序对数为k的这样自然数数列到底有多少个? 输入 第一行为两个整数n,k. 输出 写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对10000求余数后的结果. 样例输入 4 1 样例输出 3 题解 dp傻*题 设f[i][j]表示1~i组成逆序对个数为j的数列的方案数,那么考虑第i个元素,它对逆序对个

BZOJ 2431: [HAOI2009]逆序对数列【dp】

Description 对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的数列,可以很容易求出有多少个逆序对数.那么逆序对数为k的这样自然数数列到底有多少个? Input 第一行为两个整数n,k. Output 写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对10000求余数后的结果.   Sample Input样例输入 4 1 Sample Output样例输出 3 样例说明: 下列3个数列逆序对

[bzoj2431][HAOI2009][逆序对数列] (dp计数)

Description 对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的 数列,可以很容易求出有多少个逆序对数.那么逆序对数为k的这样自然数数列到底有多少个? Input 第一行为两个整数n,k. Output 写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对10000求余数后的结果. Sample Input 4 1 Sample Output 3 样例说明: 下列3个数列逆序对数都为1:分别是1

bzoj2431: [HAOI2009]逆序对数列(DP)

一眼题...f[i][j]前i个数有j个逆序对的数量 f[i][j]=sigma(f[i-1][j-k]){1<=k<=i} 维护一个前缀和即可 #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int ma

【BZOJ2431】【HAOI2009】逆序对数列 DP

题目大意 问你有多少个由\(n\)个数组成的,逆序对个数为\(k\)的排列. \(n,k\leq 1000\) 题解 我们考虑从小到大插入这\(n\)个数. 设当前插入了\(i\)个数,插入下一个数可以形成\(0,1,\ldots,i-1\)个逆序对. \[ f_{i,j}=\sum_{k=j-i+1}^jf_{i-1,k} \] 用前缀和优化即可. 时间复杂度:\(O(nk)\) UPD:这个问题可以做到\(O(n\log n)\)(FFT)或\(O(n\sqrt n)\)(五边形数定理).(

[luogu2513 HAOI2009] 逆序对数列 (计数dp)

题目描述 对于一个数列{ai},如果有i 输入输出格式 输入格式: 第一行为两个整数n,k. 输出格式: 写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对10000求余数后的结果. 输入输出样例 输入样例#1: 4 1 输出样例#1: 3 说明 样例说明: 下列3个数列逆序对数都为1:分别是1 2 4 3 :1 3 2 4 :2 1 3 4: 测试数据范围 30%的数据 n<=12 100%的数据 n<=1000,k<=1000 f[i][j] 表示i的排列有j

【bzoj3295】 Cqoi2011—动态逆序对

http://www.lydsy.com/JudgeOnline/problem.php?id=3295 (题目链接) 题意 给出某种排列,按照某种顺序依次删除m个数,在每次删除一个数前统计序列中逆序对对个数. Solution 作为一个CDQ分治的初学者,我毫不犹豫的%了LCF的题解. 这里介绍下三维偏序的求法:一维排序,二维归并,三维树状数组. 排序维护x维之后,递归处理: 1.在处理区间[L,R]的时候,先二分区间[L, (L+R)/ 2],递归求这个左区间(二分的原因是我在维护y维的时候

BZOJ1786: [Ahoi2008]Pair 配对/1831: [AHOI2008]逆序对

这两道题是一样的. 可以发现,-1变成的数是单调不降. 记录下原有的逆序对个数. 预处理出每个点取每个值所产生的逆序对个数,然后dp转移. #include<cstring> #include<iostream> #include<cstdio> #include<map> #include<cmath> #include<algorithm> #define rep(i,l,r) for (int i=l;i<=r;i++)