zoukankan      html  css  js  c++  java
  • 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
    ", slove(r) - slove(l - 1));
        }
        return 0;
    }
     
  • 相关阅读:
    C # 编码 习惯[转载]
    SilverLight 学习笔记 (一) 画刷:Brush
    55条SEO必备技巧
    c# 不安全代码之指针
    VMare 硬盘扩展和压缩、网络配置
    Win7 防护墙设置——Ping命令,Http访问
    Vs2010 sp1补丁太让人纠结
    不可小瞧的GetHashCode函数
    .NET中颜色的转换方法总结
    VS2010 如何制作项目模板
  • 原文地址:https://www.cnblogs.com/albert-biu/p/10341606.html
Copyright © 2011-2022 走看看