利用分治求一次逆序数,然后每次把第一个元素放到末尾,设该交换元素的值为x,设上一次求得的逆序数为y,那么此时的逆序数等于y - x + (n - x - 1),减去x是因为x作为第一个元素,其后共有x个元素小于x,移动x会导致逆序数减少x个,而加上 (n - x - 1) 是因为将x移动到末尾,其前面(n - 1)个元素中会有(n - x - 1)个元素大于x。

此题的复杂度在于求第一次逆序数O(nlgn),后面每次移动元素求更新后的逆序数时间是O(1),因此总的复杂度为(nlgn)。

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 5000 +5;
int a[maxn], c[maxn];

int solve(int l, int r){
    int mid = (l + r) / 2;
    if( l == r) return 0;
    int ans = 0;
    ans += solve(l, mid) + solve(mid + 1, r);

    // Merge
    int b[maxn];
    int x = l, y= mid + 1;
    int  k = 0;
    while(x <= mid && y <= r){
        if(a[x] <= a[y]){
            b[k++] = a[x++];
        }
        else {
            ans += mid + 1 - x;
            b[k++] = a[y++];
        }
    }

    while(x <= mid) b[k++] = a[x++];
    while(y <= r) b[k++] = a[y++];
    k = 0;
    for(int i = l; i <= r; ++i) a[i] = b[k++];
    return ans;
}
int main(){
    int n;
    while(scanf("%d", &n) == 1){
        for(int i = 0; i < n; ++i){
            scanf("%d", &a[i]);
        }
        memcpy(c, a, sizeof(a));
        int ans = solve(0, n-1);
        int x = ans;
        for(int i = 0; i < n; ++i){
            ans = min(ans, x + n - 1 - 2 * c[i]);
            x = x + n - 1 - 2 * c[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

线段树也能做这个题,每个区间保存的值代表[l, r]中总出现多少元素,每次加入x元素时,查找区间[x + 2, n]即可得到该元素的加入会增加多少逆序数。

贴上线段树代码:

#include<cstdio>
#define min(x,y) (x) < (y) ? x : y
const int maxn = 4 * 5000 + 5;

int a[5000 + 5];
struct node{
    int L, R;
    int cnt;
}t[maxn];

void Build(int l, int r, int cur){
    t[cur].L = l, t[cur].R = r;
    t[cur].cnt = 0;
    if(l == r) return;
    int mid = (l + r) / 2;
    Build(l, mid, cur << 1);
    Build(mid + 1, r, (cur << 1) + 1);
}

void add(int c, int cur){
    int l = t[cur].L, r = t[cur].R;
    t[cur].cnt++;
    if(l == r) return;
    int mid = (l + r) / 2;
    if(c <= mid) add(c, cur << 1);
    else add(c, (cur << 1) + 1);
}

int find1(int l, int r, int cur){ //search the Inversion (logn)
    int l1 = t[cur].L, r1 = t[cur].R;
    if(l == l1 && r == r1) return t[cur].cnt;
    int mid = (l1 + r1) / 2;
    if(r <= mid) return find1(l, r, cur << 1);
    else if(l >= mid + 1) return find1(l, r, (cur << 1) + 1);
    else return find1(l, mid, cur << 1) + find1(mid + 1, r, (cur << 1) + 1);
}

int main(){
    int n;
    while(scanf("%d", &n) == 1){
        Build(1, n, 1);
        int ans = 0;
        for(int i = 0; i < n; ++i){
            scanf("%d", &a[i]);
            a[i]++;
            add(a[i], 1);
            if(a[i] + 1 <= n) ans += find1(a[i] + 1, n, 1);
        }
        int x = ans;
        for(int i = 0; i < n; ++i){
            ans = min(ans, x + n + 1 - 2 * a[i]);
            x = x + n + 1 - 2 * a[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

如有不当之处欢迎指出!

hdu1394 分治 or 线段树的更多相关文章

  1. 【BZOJ2001】[HNOI2010]城市建设(CDQ分治,线段树分治)

    [BZOJ2001][HNOI2010]城市建设(CDQ分治,线段树分治) 题面 BZOJ 洛谷 题解 好神仙啊这题.原来想做一直不会做(然而YCB神仙早就切了),今天来怒写一发. 很明显这个玩意换种 ...

  2. BZOJ3711 Druzyny 最大值分治、线段树

    传送门 被暴力包菜了,然而还不会卡-- 有一个很暴力的DP:设\(f_i\)表示给\(1\)到\(i\)分好组最多可以分多少组,转移枚举最后一个组.接下来考虑优化这个暴力. 考虑:对于每一个位置\(i ...

  3. BZOJ4860 BJOI2017 树的难题 点分治、线段树合并

    传送门 只会线段树……关于单调队列的解法可以去看“重建计划”一题. 看到路径长度$\in [L,R]$考虑点分治.可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边 ...

  4. 线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

    闲话 stO猫锟学长,满脑子神仙DS 网上有不少Dalao把线段树分治也归入CDQ分治? 还是听听YCB巨佬的介绍: 狭义:只计算左边对右边的贡献. 广义:只计算外部对内部的贡献. 看来可以理解为广义 ...

  5. [BJOI2017]树的难题 点分治,线段树合并

    [BJOI2017]树的难题 LG传送门 点分治+线段树合并. 我不会写单调队列,所以就写了好写的线段树. 考虑对于每一个分治中心,把出边按颜色排序,这样就能把颜色相同的子树放在一起处理.用一棵动态开 ...

  6. 【BZOJ4317】Atm的树 动态树分治+二分+线段树

    [BZOJ4317]Atm的树 Description Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree…… 于是,一天晚上他梦到自己被关在了一个有根树中,每条路径 ...

  7. luoguU60884 【模板】动态点分治套线段树

    题目连接:https://www.luogu.org/problemnew/show/U60884 题意:有N个点,标号为1∼N,用N−1条双向带权通道连接,保证任意两个点能互相到达. Q次询问,问从 ...

  8. [CF1303G] Sum of Prefix Sums - 点分治,李超线段树

    给定一棵 \(n\) 个点的带点权的树,求树上的路径 \(x_1,...,x_k\) ,最大化 \(\sum_{i=1}^k ia_{x_i}\) Solution 树上路径问题可用点分治. 考虑如何 ...

  9. ACM学习历程—HDU5696 区间的价值(分治 && RMQ && 线段树 && 动态规划)

    http://acm.hdu.edu.cn/showproblem.php?pid=5696 这是这次百度之星初赛2B的第一题,但是由于正好打省赛,于是便错过了.加上2A的时候差了一题,当时有思路,但 ...

随机推荐

  1. GPU 实现 RGB -- YUV 转换 (OpenGL)

    GPU 实现 RGB -- YUV 转换 前言 RGB --> YUV 转换的公式是现成的,直接在 CPU 端转换的话,只需要遍历每个像素,得到新的 YUV 值,根据其内存分布规律,合理安排分布 ...

  2. zabbix监控windows agent安装配置

    下载Windows的zabbix客户端 下载地址:http://www.zabbix.com/download.php 从官方下载Zabbix Agent后,压缩包里面有2个目录,bin和conf,c ...

  3. VMware10不能安装64位(linux)系统,提示此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态

    今天下载VM10准备安装Ubuntu14.04,一如既往的进行安装,突然发现出现了问题:此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态,具体如图: 如图中提示可重启电脑进入B ...

  4. poj2479 最大子段和

    题意:给定一个数列.求出数列中不相交的两个子段和,要求和最大 解题思路:对每一个i来说,求出[0-i-1]的最大子段和以及[i-n-1]的最大子段和,再加起来,求出最大的一个.[0-i-1]的最大子段 ...

  5. JAVA并发编程学习笔记------线程的三种创建方式

    创建线程一般有如下几个方式: 1. 通过继承Thread类来创建一个线程: /** * 步骤1:定义一个继承Thread类的子类 * 步骤2:构造子类的一个对象 * 步骤3:启动线程: * */ pu ...

  6. linux下硬盘uuid查看及修改设置

    查看硬盘UUID 方法一:ls -l /dev/disk/by-uuid方法二:blkid /dev/sdb1 修改硬盘UUID: uuidgen 会返回一个合法的 uuid,结合 tune2fs 可 ...

  7. xBIM IFC 墙壁案例

    目录 xBIM 应用与学习 (一) xBIM 应用与学习 (二) xBIM 基本的模型操作 xBIM 日志操作 XBIM 3D 墙壁案例 xBIM 格式之间转换 xBIM 使用Linq 来优化查询 x ...

  8. cdcqの省选膜你赛

    cdcqの省选膜你赛 比赛当天因为在杠hnoi2016的大数据结构没有参加,今天补了一下.挺好玩的虽然不看一句话题意的话真的卡读题 此生无悔入东方,来世愿生幻想乡 2651. 新史「新幻想史 -现代史 ...

  9. 3.C++内联函数,默认参数,占位参数

    本章主要内容: 1)内联函数(替代宏代码段) 2)默认参数 3)占位参数 1.C++的内联函数分析 1.1讲解内联函数之前,首先回忆下之前讲的define宏定义: 之前讲过宏定义会经过预处理器进行文本 ...

  10. Ubuntu的Java环境变量

    新架构要上线了,这两天开始准备分析一下了,今天是直接进到JAVA_HOME的lib目录执行的java -cp sa-jdi.jar sun.jvm.hotspot.HSDB,然后报了个错: 这是哪来的 ...