首页 > 其他分享 >从CF1676H2看逆序对的两种求法

从CF1676H2看逆序对的两种求法

时间:2024-03-07 13:46:52浏览次数:15  
标签:std info int void CF1676H2 求法 init vector 逆序

Problem - 1676H2 - Codeforces

思路

原问题可以以直接转化成求 \(a_i >= a_j(i <j)\) 的数量。

归并排序

原理很直接,归并排序就是为了减少逆序对的数量,所以直接在操作的时候记录减少的数量即可

排序后的数组无逆序对,归并排序的合并操作中,每次后段首元素被作为当前最小值取出时,前段剩余元素个数之和即是合并操作减少的逆序对数量

但是实现比较麻烦

std::vector<int> a, temp;

int n;

i64 merge(int left, int mid, int right) {
    int i(left), j(mid), k(left);
    i64 count(0);
    while(i <= mid - 1 and j <= right) {
        if (a[i] < a[j]) {
            temp[k++] = a[i++];
        }
        else {//因为题目要求a[i] >= a[j]
            temp[k++] = a[j++];
            count += mid - i;//又因为合并操作已保证左区间有序,所以i之后到mid的a[i]都大于等于a[j]
        }
    }
    while(i <= mid - 1) 
        temp[k++] = a[i++];
    while(j <= right) 
        temp[k++] = a[j++];
    for (i = left; i <= right; i++) 
        a[i] = temp[i];
    return count;
}

i64 mergeSort(int left, int right) {
    int mid(0);
    i64 count(0);
    if (right > left) {
        mid = left + right >> 1;
        count = mergeSort(left, mid);
        count += mergeSort(mid + 1, right);
        count += merge(left, mid + 1, right);
    }
    return count;
}

i64 aInversion(int n) {
    temp.resize(n);
    return mergeSort(0, n - 1);
}

void solve() {
    std::cin >> n;
    a.resize(n);
    for (auto& x : a) std::cin >> x;
    std::cout << aInversion(n) << '\n';
}

树状数组or线段树

题目说了\(a_i \leq n\)

考虑用 \(a_i\) 来作为一段 \(01\) 前缀和中 \(1\) 的下标,即通过前缀和来计数

从后往前扫,每次先查询包括该下标到 \(1\)​ (因为要求 \(a_i >= a_j\)​)的前缀和,显然就是对于当前数字的逆序对个数

  • 树状数组
template <typename T>
struct Fenwick {
    int n;
    std::vector<T> a;
    
    Fenwick(int n_ = 0) {
        init(n_);
    }
    
    void init(int n_) {
        n = n_;
        a.assign(n, T{});
    }
    
    void add(int x, const T &v) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] = a[i - 1] + v;
        }
    }
    
    T sum(int x) {
        T ans{};
        for (int i = x; i > 0; i -= i & -i) {
            ans = ans + a[i - 1];
        }
        return ans;
    }
};

void solve() {
    int n;
    std::cin >> n;
    Fenwick<int> fen(n);
    std::vector<int> a(n);
    for (auto& x : a) std::cin >> x, x--;
    i64 ans(0);
    for (int i = n - 1; i >= 0; i--) {
        ans += fen.sum(a[i] + 1);//这里是因为前面对数据做了--防越界处理,而该fenwick的实现中查询是右开的
        fen.add(a[i], 1);
    }
    std::cout << ans << '\n';
}
  • 线段树
template<class Info>
struct SegmentTree {
    int n;
    std::vector<Info> info;
    SegmentTree() : n(0) {}
    SegmentTree(int n_, Info v_ = Info()) {
        init(n_, v_);
    }
    template<class T>
    SegmentTree(std::vector<T> init_) {
        init(init_);
    }
    void init(int n_, Info v_ = Info()) {
        init(std::vector(n_, v_));
    }
    template<class T>
    void init(std::vector<T> init_) {
        n = init_.size();
        info.assign(4 << std::__lg(n), Info());
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (r - l == 1) {
                info[p] = init_[l];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m, r);
            pull(p);
        };
        build(1, 0, n);
    }
    void pull(int p) {
        info[p] = info[2 * p] + info[2 * p + 1];
    }
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = info[p] + v;
            return;
        }
        int m = (l + r) / 2;
        if (x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }
    void modify(int p, const Info &v) {
        modify(1, 0, n, p, v);
    }
    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l >= y || r <= x) {
            return Info();
        }
        if (l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
    }
    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n, l, r);
    }
};

struct Info {
    i64 sum;
};

Info operator+(Info a, Info b) {
    a.sum += b.sum;
    return a;
}

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (auto& x : a) std::cin >> x, x--;
    i64 ans(0);
    SegmentTree<Info> T(n + 1);
    for (int i = n - 1; i >= 0; i--) {
        ans += T.rangeQuery(0, a[i] + 1).sum;//这里是因为前面对数据做了--防越界处理,而该segmentTree的实现中查询是左闭右开的
        T.modify(a[i], {1});
    }
    std::cout << ans << '\n';
}

标签:std,info,int,void,CF1676H2,求法,init,vector,逆序
From: https://www.cnblogs.com/kdlyh/p/18058721

相关文章

  • 对最大公约数求法和扩展欧几里得算法的简要概述
    目录1.最大公约数(gcd)1.1更相减损术时间复杂度分析1.2辗转相除法(欧几里得算法)时间复杂度分析2.最小公倍数(lcm)3.裴蜀定理(贝祖定理)3.1扩展欧几里得算法(exgcd)1.最大公约数(gcd)数论中,通常用\(d\|\a\)表示\(d\)能整除\(a\),即\(......
  • 小苯的逆序对
    小苯的逆序对题目描述小苯有一个长度为$n$的排列$p$。他很想知道这个排列中有多少个逆序对满足互素。形式化的,有多少个满足$(i<j)$且$(a_i>a_j)$且$gcd(a_i,a_j)=1$的$(i,j)$对。输入描述:输入包含两行。第一行一个正整数$n(1\leqn\leq2\times10^5)$......
  • P3157 [CQOI2011] 动态逆序对 题解
    题目链接:动态逆序对常见经典题,理解一个数对逆序对贡献计算即可。对于一个数而言,它在一个序列中的逆序对有两部分贡献,一部分为前面比它严格大的数,另一部分为后面比它严格小的数,有道二莫题也是基于此去考虑的。考虑最开始知道了总逆序数,每次删除一个数逆序数会减少两部分值,显然就......
  • 权值树状数组例题——逆序对
    目录问题引入思路一览具体情况code部分补充问题引入(洛谷P1908)[https://www.luogu.com.cn/problem/P1908],简单来说就是给一个数列,求出逆序对的数量思路一览BF做法:遍历数组中的每一个数,对于每一个数,再次遍历前面的数,时间复杂度是n2归并排序:这个...,不太了解,以后明白了再填坑......
  • 逆序对/归并排序
    逆序对定义:对于给定的一段正整数序列,逆序对就是序列中$a_i>a_j$且$i<j$的有序对。P1908逆序对-洛谷|计算机科学教育新生态(luogu.com.cn)#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=2e6+10;intn,m,a[N],c[N],res,num;void......
  • 逆序句子,但单词顺序不变
    如,输入:Ilikecoding!输出:coding!likeI#define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>#include<assert.h>#include<string.h>voidReverse_arr(char*left,char*right){ assert(left); assert(right); chartem=0; while(le......
  • 一道逆序对题
    CF1915Fhttps://codeforces.com/contest/1915/problem/F先了解什么是逆序对https://www.luogu.com.cn/problem/P1908即i<j但a[i]>a[j]的数对。求解的方法:树状数组求解。树状数组维护的其实就是一个区间内比他大并且比他先出现的值,每次查询的范围就是在原数组的下标之前......
  • 对于 EI K 逆序对排列计数的另一种自然求和方法的理解
    有一个简单的\(O(n^3)\)DP,考虑\(f_{x+1,k}=\sum_{j=0}^{x}f_{x,k-j}\),利用前缀和优化即可。考虑这实际上是\(f_{x+1}(k)=f_x(k)*\frac{1-k^{x+1}}{1-k}\),于是答案实际上为:\[[x^k]f(x)=\prod_{i=1}^n\frac{1-x^i}{1-x}\]接下来的方法来自......
  • 输入一个整数,将这个整数以字符串的形式逆序输出 程序不考虑负数的情况,若数字含有0,则逆
    描述输入一个整数,将这个整数以字符串的形式逆序输出程序不考虑负数的情况,若数字含有0,则逆序形式也含有0,如输入为100,则输出为001数据范围:0\len\le2^{30}-1\0≤n≤230−1输入描述:输入一个int整数输出描述:将这个整数以字符串的形式逆序输出点击查看代码#include<i......
  • 设计一个函数实现字符串的逆序,并且不可以使用库函数
    #define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>voidreverse_string(chararr[],intsz){ intleft=0; intright=sz-1; while(arr[left]<arr[right]) { inttmp=arr[left]; arr[left]=arr[right]; arr[right]=tmp; left++; ......