UPC3457: Next K Permutation

题目描述

n 个数有 n! 种全排列情况,对所有排列排序后求第 L 个到第 R 个排列中逆序对数量之和。
逆序对定义(摘自 wiki):

设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i,j 使得 1≤i<j≤n 而且 Ai>Aj,则 (Ai,Aj) 这一个有序对称为 A 的一个逆序对,也称作逆序。逆序对的数量称作逆序数。

 

输入

第一行 case 数量 T。
接下来每一行有 3 个数,n,L,R (3≤n≤12,1≤L≤R≤109)。

 

输出

输出逆序对总数。

 

样例输入

3
3 3 5
6 720 720
8 14625 17743

样例输出

5
15
38745

 

提示

样例 1 说明:
3 个数所有排列排序后及其逆序对个数:
    (1,2,3): 0;
    (1,3,2): 1;
    (2,1,3): 1;
    (2,3,1): 2;
    (3,1,2): 2;
    (3,2,1): 3.
第 3 个到第 5 个排列逆序对数量之和为 1+2+2=5。

 

来源/分类

#include "bits/stdc++.h"

using namespace std;
typedef long long ll;
ll fac[20], invnum[20];//fac为阶乘,invnum[i]为长度为i的数组的所有排列情况的逆序对之和,
int n;
int vis[20];

void init() {
    fac[0] = 1;
    fac[1] = 1;
    invnum[1] = 0;
    invnum[0] = 0;
    for (int i = 2; i <= 12; i++) {
        fac[i] = i * fac[i - 1];
        invnum[i] = fac[i - 1] * (i * (i - 1)) / 2 + i * invnum[i - 1];//将数组分为两段,前面一段长度为1,后面一段长度为i-1
        //fac[i-1] * (i * (i - 1)) / 2 任取两数,将大的放在第一个,乘以后面i-1个数的全排列,得其对总逆序数的贡献
        //i * invnum[i - 1] 任取一数放在第一个,其余i-1个数对总逆序数的贡献
    }
}

ll slove(ll t) {
    ll res = 0;
    int pre[20];
    for (int i = 1; i <= n; i++) {//枚举前缀长度
        for (int j = 1; j <= n; j++) {//枚举前缀元素
            bool flag = false;
            for (int l = 1; l < i; l++) {
                if (pre[l] == j) {
                    flag = true;//元素只能出现一次
                    break;
                }
            }
            if (flag) continue;
            pre[i] = j;
            if (t < fac[n - i]) break;
            else {//将数列分为前i个与后n-i个两部分计算总逆序数
                t -= fac[n - i];
                res += invnum[n - i];
                ll cnt = 0;
                for (int k = 1; k <= i; k++) {
                    for (int l = k; l <= i; l++) {
                        if (pre[l] < pre[k]) cnt++;
                    }
                }
                memset(vis, 0, sizeof(vis));
                for (int k = 1; k <= i; k++) {
                    vis[pre[k]] = 1;
                }
                for (int k = 1; k <= n; k++) {
                    for (int l = 1; l < k; l++) {
                        if (vis[k] && !vis[l]) cnt++;
                    }
                }
                res += cnt * fac[n - i];
            }
        }
    }
    return res;
}

int main() {
    freopen("input.txt", "r", stdin);
    int _;
    scanf("%d", &_);
    ll l, r;
    init();
    while (_--) {
        scanf("%d %lld %lld", &n, &l, &r);
        printf("%lld\n", slove(r) - slove(l - 1));
    }
    return 0;
}

 

 
posted @ 2019-01-31 13:33  Albert_liu  阅读(189)  评论(0编辑  收藏  举报