zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    python语法基础-常用模块-re模块
    python语法基础-常用模块-长期维护
    python语法基础-函数-内置函数和匿名函数-长期维护
    python语法基础-函数-递归函数-长期维护
    python语法基础-函数-迭代器和生成器-长期维护
    python语法基础-函数-装饰器-长期维护
    python语法基础-函数-进阶-长期维护
    python语法基础-函数-基础-长期维护
    Python之路--目录
    mysql数据库-索引-长期维护
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196335.html
Copyright © 2011-2022 走看看