[BZOJ5255][FJWC2018]全排列(组合计数+dp)

Solution

  • 组合数学 \(+\) 前缀和优化\(dp\)
  • 考虑\(P1[l...r]\)\(P2[l...r]\)离散化后的排列\(P[1...r-l+1]\)
  • \(i=r-l+1\)
  • 那么离散化后为\(P\)的子串会在\(C(n,i)*(n-i)!\)种长度为\(n\)的排列中出现(假设这个子串必须放在\([1...i]\)
  • 解释:\(1\)\(n\)中选\(i\)个数,先排成和\(P\)相似的子串
  • 为防止一种长度为\(n\)的排列中,出现多个离散化后为\(P\)的子串导致一种排列算多次,固定这个子串放在位置\([1...i]\)
  • 剩下的\(n-i\)个位置,\(n-i\)个数可以随便排列
  • \(sum[i][j]\)表示\(1...i\)的所有排列中,逆序对数不超过\(j\)的有多少种
  • 那么答案就是\(\sum_{i=1}^{n}sum[i][min(E,i*(i-1)/2)]*(C(n,i)*(n-i)!)^2*(n-i+1)\)
  • 解释:依照上文可以构造出\((C(n,i)*(n-i)!)^2\)组合法的,且子串放在位置\([1...i]\)\((P1,P2)\)
  • 然后考虑这个子串的开头放在哪,由于\(P1\)\(P2\)的相似子串要在相同位置,所以乘上\(n-i+1\)
  • 问题转化为求\(sum[i][j]\)
  • \(f[i][j]\)表示\(1\)\(i\)的所有排列中,逆序对数正好为\(j\)的有多少种
  • 考虑枚举\(i\)放在倒数第\(k+1\)个位置
  • 那么会产生新的\(k\)组逆序对,即\(f[i][j]+=f[i-1][j-k]\)
  • \(k\)的取值为\(0\)\(i-1\),所以\(f[i][j]=sum[i-1][j]-sum[i-1][j-i]\)
  • 注意特判\(j-i<0\)的情况
  • 时间复杂度\(O(Tn+n^3)\)

Code

#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;
}
posted @ 2020-01-15 14:01  花淇淋  阅读(123)  评论(0编辑  收藏  举报