【模板】逆序队(树状数组/归并排序)

P1908 逆序对

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

输入输出格式

输入格式:

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。

输出格式:

给定序列中逆序对的数目。

输入输出样例

输入样例#1:

6
5 4 2 6 3 1

输出样例#1:

11

说明

对于50%的数据,n≤2500

对于100%的数据,n≤40000。

两种写法,上代码。

树状数组

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
const int MAXN = 40000 + 10;
int data[MAXN];
int num[MAXN];
struct T
{
    int rank;
    int num;
}cnt[MAXN];
int n;
bool cmp(T a, T b)
{
    return a.num > b.num;
}
inline int lowbit(int k)
{
    return k & (-1 * k);
}
inline int ask(int k)
{
    int x = 0;
    for(;k;k -= lowbit(k))
    {
        x += data[k];
    }
    return x;
}
inline void modify(int k, int num)
{
    for(;k <= n;k += lowbit(k))
    {
        data[k] += num;
    }
}
inline int read()
{
    int x = 0;char ch = getchar();char c = 1;
    while(ch > ‘9‘ || ch < ‘0‘) c = ch,ch = getchar();
    while(ch >= ‘0‘ && ch <= ‘9‘)x = x * 10 + ch - ‘0‘,ch = getchar();
    if(c == ‘-‘) x *= -1;
    return x;
}
inline void wri(int k)
{
    if(k)
    {
        wri(k/10);
        putchar(k%10);
    }
}
inline void wright(int k)
{
    if(k < 0) putchar(‘-‘),wri(-1 *k);
    else wri(k);
}
int ans;
int main()
{
    n = read();
    for(int i = 1;i <= n;i ++)
    {
        cnt[i].num = read();
        cnt[i].rank = i;
    }
    std::sort(cnt + 1, cnt + 1 + n, cmp);

    for(int i = 1;i <= n;i ++)
    {
        num[cnt[i].rank] = i;
    }

    for(int i = 1;i <= n;i ++)
    {
        ans += ask(num[i]);
        modify(num[i], 1);
    }
    printf("%d", ans);
    return 0;
}

归并排序

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

inline int read()
{
    int x = 0;char ch = getchar();char c = ch;
    while(ch > ‘9‘ || ch < ‘0‘)c = ch, ch = getchar();
    while(ch <= ‘9‘ && ch >= ‘0‘)x = x * 10 + ch - ‘0‘,ch = getchar();
    if(c == ‘-‘)x = x * -1;
    return x;
}
inline void wri(int k)
{
    if(k)
    {
        wri(k / 10);
        putchar(k % 10 + ‘0‘);
    }
}
inline void write(int k)
{
    if(k < 0) putchar(‘-‘),k = -1 * k;
    wri(k);
}

const int MAXN = 1000000 + 10;

int n;
int data[MAXN];
int temp[MAXN];
int ans;

void merge_sort(int x, int y)
{
    if(y - x < 1)return;
    int mid =x + (y - x) / 2;
    int p = x,q = mid + 1,i = x;
    merge_sort(x, mid);
    merge_sort(mid + 1, y);
    while(p <= mid || q <= y )
    {
        if((p <= mid && data[p] <= data[q]) || q > y)temp[i++] = data[p++];
        else temp[i++] = data[q++],ans += mid - p + 1;
    }
    for(int i = x;i<= y;i ++)data[i] = temp[i];
}

int main()
{
    n = read();
    for(int i = 1;i <= n;i ++)
    {
        data[i] = read();
    }
    merge_sort(1, n);
    write(ans);
    return 0;
}
时间: 2024-10-01 07:25:33

【模板】逆序队(树状数组/归并排序)的相关文章

逆序对 (树状数组 | | 归并排序

数组前面的一个元素 大于等于 后面的一个元素就是一个逆序对: 树状数组可以快速求前缀和,利用这一特性,可以求逆序对个数,见下: 用数组c[ i ]记录数组a[ n ]中i这一元素出现的次数 ,当a[ n ]中元素较大时可以离散化处理. 将a[ n ]从a[n -1]到a[0] 依次存到树状数组中,每存一个,对存的元素i求一次c[i]的前缀和, 这就是当前已扫描过的比i小的元素的个数,由于a[n]是倒着扫描的,所以此时比i小的元素都对应一个逆序对,统计之. #include <cstdio> #

Day2:T4求逆序对(树状数组+归并排序)

T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*(); B[J]>=B[I] 也就是求逆序对: 求逆序对的方法主要有两种: 归并排序: 树状数组: 这里两种方法都学习一下: 1.之前对于树状数组的印象就只有单点修改和区间求和 一直觉得lowbit是一个神奇的东西(至今没有搞懂原理) 上网搜了一下用树状数组求逆序对的方法,发现有一个大神写的很棒....看

bzoj 3289: Mato的文件管理 莫队+树状数组

3289: Mato的文件管理 Time Limit: 40 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先

求逆序对 (树状数组版)

基本思想和线段树求解逆序数是一样的,前一篇<求逆序对 线段树版>也介绍过,先对输入数组离散,数组里的元素都不相同可以直接hash,存在相同的数话可以采用二分. 离散化后对于每个f[i],找到f[i]+1~ n中的个数,也就是到i这个位置,一共有多少比f[i]大的数,统计之后在将f[i]的位置上的数量加1. 这样一来统计的就是类似a[i]~n的和,可以想象成 把树状数组反过来统计,即统计的时候加lowbit,更新的时候减lowbit. 还是 以POJ 2299为例. #include <i

HDU 4417 类似求逆序数的树状数组

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2250    Accepted Submission(s): 1092 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability

51nod 1290 Counting Diff Pairs | 莫队 树状数组

51nod 1290 Counting Diff Pairs | 莫队 树状数组 题面 一个长度为N的正整数数组A,给出一个数K以及Q个查询,每个查询包含2个数l和r,对于每个查询输出从A[i]到A[j]中,有多少对数,abs(A[i] - A[j]) <= K(abs表示绝对值). 题解 莫队!//其实我就是搜索"51nod + 莫队"找到的这道题-- 七级算法题! 一道320分! 你值得拥有! 题解就是--用个普通的莫队,加上树状数组来统计符合条件的数个数,就好啦. 当增加/

HihoCoder 1488 : 排队接水(莫队+树状数组)

描述 有n个小朋友需要接水,其中第i个小朋友接水需要ai分钟. 由于水龙头有限,小Hi需要知道如果为第l个到第r个小朋友分配一个水龙头,如何安排他们的接水顺序才能使得他们等待加接水的时间总和最小. 小Hi总共会有m次询问,你能帮助他解决这个问题吗? 假设3个小朋友接水的时间分别是2,3,4.如果他们依次接水,第一位小朋友等待加接水的时间是2,第二位小朋友是5,第三位小朋友是9.时间总和是16. 输入 第一行一个数T(T<=10),表示数据组数 对于每一组数据: 第一行两个数n,m(1<=n,m

Inversion (hdu 4911 树状数组 || 归并排序 求逆序对)

Inversion Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 2003    Accepted Submission(s): 787 Problem Description bobo has a sequence a1,a2,-,an. He is allowed to swap two adjacent numbers fo

Codeforces 375D Tree and Queries(DFS序+莫队+树状数组)

题目链接  Tree and Queries 题目大意  给出一棵树和每个节点的颜色.每次询问vj, kj 你需要回答在以vj为根的子树中满足条件的的颜色数目, 条件:具有该颜色的节点数量至少为kj. (莫队居然可以过) 首先转DFS序,这样就变成了区间查询. 然后直接套用莫队,求出每次询问状态下的t[],t[k]表示当前区间内拥有k个节点的颜色数量. 然后统计t[k] + t[k + 1], ..., t[MAX]即可,这个过程用树状数组维护. #include <bits/stdc++.h>