zoukankan      html  css  js  c++  java
  • 逆序对数列[HAOI2009]

    【题目描述】
    对于一个数列(a),如果有(i<j)(a_i>a_j),那么我们称(a_i)(a_j)为一对逆序对数。若对于任意一个由(1sim n)自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为(k)的这样自然数数列到底有多少个?

    【输入格式】
    第一行为两个整数(n,k)

    【输出格式】
    写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对(10000)求余数后的结果。

    题解

    挺水的吧。。。简单推一下DP方程

    (dp[i][j])表示(1sim i)的全排列中逆序对数量为(j)的有多少个

    边界:(dp[2][0]=dp[2][1]=1) 特别注意(dp[1][0]=1)

    下面是转移方程

    举个栗子吧:(3,1,2) 现在把(4)加进去 如果加在第(k)个数后面((0le kle 3)) 逆序对就会多(3-k)

    那就完事啦 (dp[i][j]=sumlimits_{k=0}^{i-1}dp[i-1][j-k])

    就比如推(dp[4][3])(4)在排列第一位,且逆序对数量为(3)的方案数就是(dp[3][0]) 因为你把(4)放第一个就一定会多产生(3)个逆序对嘛 那原来(3)个数就只能是没有逆序对了

    前缀和(O(1))转移

    (dp)数组第一维可以去掉然后离线做 虽然这个数据很水 不去掉也没事 但是有一年ACM/ICPC好像考了原题 但是卡空间

    然后离线给询问从小到大排个序 边DP边存答案 时间复杂度(O(n^2))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    inline int read() {
    	int x = 0, f = 1; char ch = getchar();
    	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
    	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
    	return x * f;
    }
    
    const ll mod = 1000000007;
    int tot, now;
    ll dp[5005], sum[5005], ans[5005];
    
    struct question{
        int n, m, ind;
    } q[5005];
    
    inline bool cmp(question a, question b) {
        return a.n < b.n;
    }
    
    inline void DP() {
    	dp[0] = dp[1] = 1;
    	for (ll j = 0; j <= 5000; j++) {
    		sum[j] = j == 0 ? 1 : 2;
    	} 
        while (now <= tot && q[now].n == 2) {
            ans[q[now].ind] = dp[q[now].m];
            now++;
        }
    	for (ll i = 3; i <= 5000; i++) {
            memset(dp, 0, sizeof(dp));
    		for (ll j = 0; j <= min(i * (i - 1) / 2, 5000ll); j++) {
    			if (j - i < 0) {
    				dp[j] = sum[j];
    			} else {
    				dp[j] = (sum[j] - sum[j - i] + mod) % mod;
    			}
    		}
            memset(sum, 0, sizeof(sum));
    		sum[0] = dp[0];
    		for (ll j = 1; j <= 5000; j++) {
    			sum[j] = (sum[j-1] + dp[j]) % mod;
    		}
            while (now <= tot && q[now].n == i) {
                ans[q[now].ind] = dp[q[now].m];
                now++;
            }
    	}	
    }
    
    int main() {
        tot = read();
    	for (int i = 1, n, m; i <= tot; i++) {
    		n = read(); m = read(); 
    		q[i] = question{n, m, i};
    	}
        sort(q + 1, q + tot + 1, cmp);
        now = 1; 
        while (now <= tot && q[now].n == 1) {
            ans[q[now].ind] = q[now].m == 0 ? 1 : 0;
            now++;
        }
        DP();
        for (int i = 1; i <= tot; i++) {
            printf("%lld
    ", ans[i]);
        }
    	return 0;
    } 
    
  • 相关阅读:
    Ansible跳板机自动部署
    nginx展示文件目录
    【转】消息钩子注册浅析
    windows临界区
    windbg定位死锁
    Windows工作集内存
    我的spring boot,杨帆、起航!
    CursorFileManager对cursor文件的读写
    eclipse执行maven install命令时跳过test
    bASE--Risk
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM53.html
Copyright © 2011-2022 走看看