题意不提。
我们可以发现,可以将最终序列分为对于第i个位置i-pi>=0与i-pi<0种两个子序列。且如果f[n]==g[n],则有两个子序列都递增。
原因是f[n]表示1-n这个排列的逆序对个数,即冒泡排序的交换次数,而每个g[i]表示将p[i]从i位置移到它应当在的p[i]位置的交换次数。
考虑将每个满足i-p[i]>0的p[i]从i位置移到p[i]位置是正确的条件,显然对于i-p[i]>0的每个p[i]必须递增,否则,会产生p[i]与p[j]交换时的交叉,使冒泡的代价增大。
若 i-p[i]<0 的p[i]不递增,它们之间会产生新的冒泡,使冒泡的代价增加。
所以就是DP了,设f[i][j]表示已放了j个数,其中最大数为i的且满足限制的方案数,显然如果j+1的位置放i-p[i]<0的,直接枚举i+1-n的数字即可。
若j+1的位置放i-p[i]>=0的数字,由于i-p[i]>=0的数字必须递增,且i递增,因此有一个必选的数字直接填入即可。
直接转移即可。
#include<bits/stdc++.h> #define MOD 1000000007 using namespace std; #define FILE "chad" set<int> S; int n, k, f[105][105]; int main() { //freopen(FILE".in","r",stdin); //freopen(FILE".out","w",stdout); int T; scanf("%d",&T); for(int tt = 0;T--;) { memset(f, 0, sizeof f); S.clear(); scanf("%d%d",&n,&k); for(int i = 1; i <= n; i++) S.insert(i); int mx = 0, flag = 1; for(int i = 1; i <= k; i++) { int x; scanf("%d",&x); if(x > mx) { mx = x; } else if(x != *S.begin()) flag = 0; S.erase(x); } f[mx][k] = flag; for(int i = 0; i <= n; i++) { for(int j = 0; j < n; j++) { if(i-j > 0) (f[i][j+1] += f[i][j]) %= MOD; for(int k = i+1; k <= n; k++) (f[k][j+1] += f[i][j]) %= MOD; } } printf("Case #%d: %d ",++tt,f[n][n]); } } //代码来自某AC代码,侵删。