【算法分析】
- 组合数学 + + + 前缀和优化 d p dp dp
- 考虑 P 1 [ l . . . r ] P1[l...r] P1[l...r]和 P 2 [ l . . . r ] P2[l...r] P2[l...r]离散化后的排列 P [ 1... r − l + 1 ] P[1...r-l+1] P[1...r−l+1]
- 令 i = r − l + 1 i=r-l+1 i=r−l+1
- 那么离散化后为 P P P的子串会在 C ( n , i ) ∗ ( n − i ) ! C(n,i)*(n-i)! C(n,i)∗(n−i)!种长度为 n n n的排列中出现(假设这个子串必须放在 [ 1... i ] [1...i] [1...i])
- 解释: 1 1 1到 n n n中选 i i i个数,先排成和 P P P相似的子串
- 为防止一种长度为 n n n的排列中,出现多个离散化后为 P P P的子串导致一种排列算多次,固定这个子串放在位置 [ 1... i ] [1...i] [1...i]
- 剩下的 n − i n-i n−i个位置, n − i n-i n−i个数可以随便排列
- 设 s u m [ i ] [ j ] sum[i][j] sum[i][j]表示 1... i 1...i 1...i的所有排列中,逆序对数不超过 j j j的有多少种
- 那么答案就是 ∑ i = 1 n s u m [ i ] [ m i n ( E , i ∗ ( i − 1 ) / 2 ) ] ∗ ( C ( n , i ) ∗ ( n − i ) ! ) 2 ∗ ( n − i + 1 ) \sum_{i=1}^{n}sum[i][min(E,i*(i-1)/2)]*(C(n,i)*(n-i)!)^2*(n-i+1) ∑i=1nsum[i][min(E,i∗(i−1)/2)]∗(C(n,i)∗(n−i)!)2∗(n−i+1)
- 解释:依照上文可以构造出 ( C ( n , i ) ∗ ( n − i ) ! ) 2 (C(n,i)*(n-i)!)^2 (C(n,i)∗(n−i)!)2组合法的,且子串放在位置 [ 1... i ] [1...i] [1...i]的 ( P 1 , P 2 ) (P1,P2) (P1,P2)
- 然后考虑这个子串的开头放在哪,由于 P 1 P1 P1和 P 2 P2 P2的相似子串要在相同位置,所以乘上 n − i + 1 n-i+1 n−i+1
- 问题转化为求 s u m [ i ] [ j ] sum[i][j] sum[i][j]
- 设 f [ i ] [ j ] f[i][j] f[i][j]表示 1 1 1到 i i i的所有排列中,逆序对数正好为 j j j的有多少种
- 考虑枚举 i i i放在倒数第 k + 1 k+1 k+1个位置
- 那么会产生新的 k k k组逆序对,即 f [ i ] [ j ] + = f [ i − 1 ] [ j − k ] f[i][j]+=f[i-1][j-k] f[i][j]+=f[i−1][j−k]
- k k k的取值为 0 0 0到 i − 1 i-1 i−1,所以 f [ i ] [ j ] = s u m [ i − 1 ] [ j ] − s u m [ i − 1 ] [ j − i ] f[i][j]=sum[i-1][j]-sum[i-1][j-i] f[i][j]=sum[i−1][j]−sum[i−1][j−i]
- 注意特判 j − i < 0 j-i<0 j−i<0的情况
- 时间复杂度 O ( T n + n 3 ) O(Tn+n^3) O(Tn+n3)
【参考程序】
#include <bits/stdc++.h>
using namespace std;
#define ll long long
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
const int e = 505, o = 124760, mod = 1e9 + 7;
int n, sum[e][o], ans, c[e][e], tst, m, fac[e];
inline void upt(int &x, int y)
{
x = y;
if (x >= mod) x -= mod;
}
inline void add(int &x, int y)
{
x += y;
if (x >= mod) x -= mod;
}
int main()
{
int i, j, k;
sum[1][0] = sum[1][1] = 1;
for (i = 2; i <= 500; i++)
{
k = i * (i - 1) / 2;
for (j = 0; j <= k; j++)
upt(sum[i][j], sum[i - 1][j] + (j >= i ? mod - sum[i - 1][j - i] : 0));
k = i * (i + 1) / 2;
for (j = 1; j <= k; j++) add(sum[i][j], sum[i][j - 1]);
}
fac[0] = 1;
for (i = 1; i <= 500; i++) fac[i] = (ll)fac[i - 1] * i % mod;
for (i = 0; i <= 500; i++)
for (j = 0; j <= i; j++)
if (i == j || j == 0) c[i][j] = 1;
else upt(c[i][j], c[i - 1][j] + c[i - 1][j - 1]);
read(tst);
while (tst--)
{
read(n); read(m);
ans = 0;
for (i = 1; i <= n; i++)
{
int len = min(m, i * (i - 1) / 2);
add(ans, (ll)sum[i][len] * (n - i + 1) % mod
* c[n][i] % mod * fac[n - i] % mod * c[n][i] % mod * fac[n - i] % mod);
}
printf("%d\n", ans);
}
return 0;
}