题意:已知N,对N的一个排列进行冒泡排序,共K趟后排成升序,求一共有多少个这样的排列(mod 20100713)。
最开始我沙茶地得出了个结论,一趟就是最大值沉底,其它不变,因此和最长下降子序列有关(其实选择排序应该是这样)。然而实际上不是。每排一趟,一个数的逆序数最多-1(沉底那个除外),因此题目就是让你构造逆序数最多的一个数的逆序数是K的N排列。
设f(i,K)为前i小的数进行排列,其中逆序数最大的一个数的逆序数小于等于K的方案数。当i小于等于K时,显然无法使得任意数的逆序数超过K,故f(i, K) = f(i-1, K) * i; i>K时,考虑将i放入这个排列,因为i是目前最大一个数,所以要使得i处于最后K+1个位置才能保证在i后面的之前放的数不超过K个。通项易得为f(N, K) = K!*(K+1)^(N-K)。然而这只是最大逆序数小于等于K的方案数,这题最巧妙的地方就是恰好等于K的方案数可以作差求得,为f(N,K)-f(N,K-1)。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
const LL tsy = 20100713;
const int MAXN = 1000010;
int T;
LL N, K;
LL jc[MAXN];
LL ksm(LL a, LL b)
{
LL res = 1;
for (; b>0; b>>=1, a=a*a%tsy)
if (b&1) res = res * a % tsy;
return res;
}
inline LL getx(LL n, LL k) {
return jc[k] * ksm(k+1, n-k) % tsy;
}
int main()
{
scanf("%d", &T);
jc[0] = 1;
for (LL i = 1; i<MAXN; ++i)
jc[i] = jc[i-1] * i % tsy;
while (T--) {
scanf("%I64d%I64d", &N, &K);
LL ans = (getx(N, K) - getx(N, K-1)) % tsy;
printf("%I64d\n", (ans + tsy) % tsy);
}
return 0;
}