题意:
把所有
n
n
n元排列按字典序从小到大排成一列,求其中有多少子串为
m
m
m元排列,
t
t
t组询问。
题解:
(1)考虑单个
n
n
n元组中的
m
m
m元组个数。
将
m
m
m元组看成一个元素,排列组合有
m
!
m!
m!的情况,现在共有
(
n
−
m
+
1
)
(n-m+1)
(n−m+1)个元素,排列组合为
(
n
−
m
+
1
)
!
(n-m+1)!
(n−m+1)!。共有
m
!
(
n
−
m
+
1
)
!
m!(n-m+1)!
m!(n−m+1)!的
m
m
m元组。
(2)考虑跨两个
n
n
n元组的
m
m
m元组个数。
假设
k
k
k为某个排列中的最后一个使得
p
k
<
p
k
+
1
>
p
k
+
2
p_k<p_{k+1}>p{k+2}
pk<pk+1>pk+2的位置。
即
p
1
,
p
2
,
p
3
.
.
.
p
k
<
p
k
+
1
>
p
k
+
2
>
p
k
+
3
.
.
.
>
p
n
p_1,p_2,p_3...p_k<p_{k+1}>p_{k+2}>p_{k+3}...>p_n
p1,p2,p3...pk<pk+1>pk+2>pk+3...>pn
那么下一个字典序的排列为:
p
1
,
p
2
,
p
3...
p
j
<
p
n
<
.
.
.
<
p
j
+
1
<
p
k
<
p
j
−
1
<
.
.
.
.
<
p
k
+
1
(
p
j
>
p
k
)
p1,p2,p3...p_j<p_n<...<p_{j+1}<p_k<p_{j-1}<....<p_{k+1}\ (p_j>p_k)
p1,p2,p3...pj<pn<...<pj+1<pk<pj−1<....<pk+1 (pj>pk)
假设
i
i
i为我们所要的
m
m
m元组开头的位置。
1.当 i < = k i<=k i<=k时:
第一个排列中存在
n
−
i
+
1
n-i+1
n−i+1个数,剩下的数在第二排列中,因为
m
<
n
m<n
m<n,所以有
m
−
(
n
−
i
+
1
)
<
k
m-(n-i+1)<k
m−(n−i+1)<k。
因为
i
<
=
k
i<=k
i<=k所以在
[
i
,
n
]
[i,n]
[i,n]内必须存在一个位置
k
k
k使得
p
k
<
p
k
+
1
p_k<p_{k+1}
pk<pk+1。方案数转化为全部的方案减去
p
i
>
p
i
+
1
>
.
.
.
>
p
n
p_i>p_{i+1}>...>p_n
pi>pi+1>...>pn的方案数。
也就是
m
!
(
全
部
的
方
案
)
−
∁
m
n
−
i
+
1
(
m
−
(
n
−
i
+
1
)
)
!
(
m
个
数
选
出
n
−
i
+
1
个
数
在
第
一
个
排
列
中
,
且
为
降
序
排
列
的
方
案
)
m!(全部的方案)-\complement_m^{n-i+1}(m-(n-i+1))!\ (m个数选出n-i+1个数在第一个排列中,且为降序排列的方案)
m!(全部的方案)−∁mn−i+1(m−(n−i+1))! (m个数选出n−i+1个数在第一个排列中,且为降序排列的方案)
剩下的
n
−
m
n-m
n−m个数随便排列。
于是此时的方案数为
(
n
−
m
)
!
(
m
!
−
∁
m
n
−
i
+
1
(
m
−
(
n
−
i
+
1
)
)
!
)
(n-m)!(m!-\complement_m^{n-i+1}(m-(n-i+1))!)
(n−m)!(m!−∁mn−i+1(m−(n−i+1))!)
2.当 i > = k + 1 i>=k+1 i>=k+1时:
此时
p
k
p_k
pk不在
m
m
m元组中,
p
k
>
m
p_k>m
pk>m,而
p
j
>
p
k
>
m
p_j>p_k>m
pj>pk>m,所以
p
j
p_j
pj也不再
m
m
m元组中。
同样:(标黄的是m元组出现的范围)
p
1
,
p
2
,
p
3
.
.
.
p
k
<
p
k
+
1
>
p_1,p_2,p_3...p_k<p_{k+1}>
p1,p2,p3...pk<pk+1>
p
k
+
2
>
p
k
+
3
.
.
.
>
p
n
p_{k+2}>p_{k+3}...>p_n
pk+2>pk+3...>pn
那么下一个字典序的排列为:
p
1
,
p
2
,
p
3
p1,p2,p3
p1,p2,p3
.
.
.
p
j
<
p
n
<
.
.
.
<
p
j
+
1
<
p
k
<
p
j
−
1
<
.
.
.
.
<
p
k
+
1
(
p
j
>
p
k
)
...p_j<p_n<...<p_{j+1}<p_k<p_{j-1}<....<p_{k+1}\ (p_j>p_k)
...pj<pn<...<pj+1<pk<pj−1<....<pk+1 (pj>pk)
这就意味着在第二个排列中,最后一个元素的下标为
i
+
m
−
1
−
n
<
j
i+m-1-n<j
i+m−1−n<j
前
n
−
i
+
1
n-i+1
n−i+1个数是递减的,剩下
(
m
−
(
n
−
i
+
1
)
)
(m-(n-i+1))
(m−(n−i+1))个数没有限制,方案数为
(
m
−
(
n
−
i
+
1
)
)
!
(m-(n-i+1))!
(m−(n−i+1))!。对于
m
m
m元组以外的
m
−
n
m-n
m−n个数,必须存在一个
k
k
k使得
p
k
<
p
k
+
1
p_k<p_{k+1}
pk<pk+1,方案数为
(
(
n
−
m
)
!
−
1
)
((n-m)!-1)
((n−m)!−1)
i
i
i的范围:
第一个排列中的数要小于
m
m
m.
(
n
−
i
+
1
)
>
=
m
−
1
,
i
>
=
n
+
2
−
m
(n-i+1)>=m-1,i>=n+2-m
(n−i+1)>=m−1,i>=n+2−m
最后答案就是
m
!
(
n
−
m
+
1
)
!
+
∑
i
=
n
+
2
−
m
n
(
(
n
−
m
)
!
(
m
!
−
∁
m
n
−
i
+
1
(
m
−
(
n
−
i
+
1
)
)
!
)
+
∁
m
n
−
i
+
1
(
m
−
(
n
−
i
+
1
)
)
!
(
(
n
−
m
)
!
−
1
)
)
m!(n-m+1)!+\sum_{i=n+2-m}^n((n-m)!(m!-\complement_{m}^{n-i+1}(m-(n-i+1))!) +\complement_{m}^{n-i+1}(m-(n-i+1))!((n-m)!-1))
m!(n−m+1)!+∑i=n+2−mn((n−m)!(m!−∁mn−i+1(m−(n−i+1))!)+∁mn−i+1(m−(n−i+1))!((n−m)!−1))
化简得:
m
!
(
n
−
m
+
1
)
!
+
(
m
−
1
)
(
n
−
m
)
!
m
!
−
m
!
∑
i
=
1
m
−
1
1
i
!
m!(n-m+1)!+(m-1)(n-m)!m!-m!\sum_{i=1}^{m-1}\frac{1}{i!}
m!(n−m+1)!+(m−1)(n−m)!m!−m!∑i=1m−1i!1
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 1000005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
ll a[maxn],b[maxn],f[maxn];
void init(){
a[0]=1;
for(ll i=1;i<=1000000;i++)a[i]=a[i-1]*i%mod;
b[1000000]=q_pow(a[1000000],mod-2,mod);
for(ll i=999999;i>=0;i--)b[i]=b[i+1]*(i+1ll)%mod;
for(int i=1;i<=1000000;i++)f[i]=(f[i-1]+b[i])%mod;
}
ll C(ll n,ll m){
return a[n]*b[m]%mod*b[n-m]%mod;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
ll n,m;
init();
cin>>t;
while(t--){
cin>>n>>m;
ll ans=a[m]*a[n-m+1]%mod+(m-1)*a[n-m]%mod*a[m]%mod-a[m]*f[m-1]%mod;
ans=(ans%mod+mod)%mod;
cout<<ans<<"\n";
}
return 0;
}