• P2448 无尽的生命


    Description

    小 a有一个长度无限长的序列 p = (1, 2, 3, 4 ……),初始时 pi = i
    给出 m 个操作,每次交换两个位置的数
    询问最后序列逆序对的个数

    Solution

    忘了可以树状数组直接做了.所以写了很麻烦的线段树.
    大概写一下怎么做, 因为细节比较多.

    我们发现一次交换的实际上是交换了两个位置上的数.
    我们可以将所有的位置分成三类:

    • 有的位置会被改变(交换), 也对答案有贡献;

    • 有的位置不会被改变, 也不会对答案有贡献;

    • 有的位置不会被改变, 但是对答案有贡献.

    • 第一类是所有的操作会交换的位置;

    • 第二类是被改变的第一个和最后一个位置往左和往右的数;

    • 第三类是不会被直接改变, 但是其左右都有被改变的数.

    举个例子:交换2和5位置, 数列变成(1,5,3,4,2,6,7,cdots).
    位置(2, 5)属于第一类, 位置(1,6,7,cdots)属于第二类, 位置(3, 4)属于第三类(因为与5位置形成逆序对)

    • 对于不会被改变也没有影响的数, 忽略存在就好了.
    • 对于不会被改变但是有影响的位置, 这些位置的行为表现出来像是一个整体(会同时对另一个位置产生或不产生逆序对).
      所以就把他们起来, 看成是一个特殊的数字就好了.

    所以就将这些涉及到的位置离散化, 在离散化后按要求交换这些位置上的数形成一个数列,利用树状数组/线段树求逆序对即可.

    至于怎么离散化, 看代码就好了

    Code

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 1e6;
    struct Node {
        long long val;
        Node *ls, *rs;
        Node(int _v = 0, Node *_ls = nullptr, Node *_rs = nullptr) :
            val(_v), ls(_ls), rs(_rs) { }
        void pushup() {
            val = ls->val + rs->val;
        }
        void mod(int k) { val += k; }
    };
    class Tree { // 普通的单调修改区间查询线段树
        int n;
        Node* root;
     #define LS l, mid, node->ls
     #define RS mid + 1, r, node->rs
        void build(int l, int r, Node* node) {
            if (l == r) return;
            int mid = l + r >> 1;
            node->ls = new Node();
            node->rs = new Node();
            build(LS), build(RS);
        }
        void insert(int l, int r, Node* node, int p, int k) {
            if (l == r) return node->mod(k);
            int mid = l + r >> 1;
            if (p <= mid) insert(LS, p, k);
            if (p >  mid) insert(RS, p, k);
            node->val = node->ls->val + node->rs->val;
        }
        long long query(int l, int r, Node* node, int L, int R) {
            if (l >= L and r <= R) 
                return node->val;
            int mid = l + r >> 1;
            long long res = 0;
            if (L <= mid) res += query(LS, L, R);
            if (R >  mid) res += query(RS, L, R);
            return res;
        }
      public:
        Tree(int _n) : n(_n), root(new Node()) {}
        void build() {
            build(1, n, root);
        }
        long long query(int l, int r) {
            return query(1, n, root, l, r);
        }
        void insert(int p, int k) {
            insert(1, n, root, p, k);
        }
    };
    struct Operate {
        int l, r;
        Operate(int _ = 0, int __ = 0) :
            l(_), r(__) {}
    }Opt[N];
    struct Element {
        int v, siz;
        Element(int _v = 0, int _s = 0) :
            v(_v), siz(_s) { }
        bool operator < (const Element& o) const {
            return v < o.v;
        }
    }P[N];
    
    int A[N], seq[N];
    
    int main () {
        int n;
        scanf("%d", &n);
        int tot = 0;
        for (int i = 1, u, v, c; i <= n; i += 1) {
            scanf("%d%d", &u, &v);
            Opt[i] = Operate(u, v);
            A[++tot] = u, A[++tot] = v;
        }
        sort(A + 1, A + tot + 1);
        int cnt = unique(A + 1, A + tot + 1) - A - 1; // 被直接交换的位置, 也就是第一类
        int total = 0;
        for (int i = 1; i <= cnt; i += 1) {
            P[++total] = Element(A[i], 1); // 第一类
            if (A[i + 1] > A[i] + 1) // A[i] 和A[i+1]之间的是第三类
                P[++total] = Element(A[i] + 1, A[i + 1] - A[i] - 1);  // A[i+1]-A[i]-1是这一段的个数
        }
    #define Find(x) lower_bound(P + 1, P + total + 1, Element(x, 0)) - P
        Tree* T = new Tree(total); // 建线段树
        T->build();
        for (int i = 1; i <= total; i += 1)
            seq[i] = i;
        for (int i = 1, u, v; i <= n; i += 1) {
            u = Find(Opt[i].l), v = Find(Opt[i].r); // 按要求交换
            swap(seq[u], seq[v]);
        }
        long long res = 0;
        for (int i = 1; i <= total; i += 1) {
            T->insert(seq[i], P[seq[i]].siz); 
            res += 1ll * P[seq[i]].siz * T->query(seq[i] + 1, total);
        }
        printf("%lld
    ", res);
        return 0;
    }
    
  • 相关阅读:
    线程池
    非XA式Spring分布式事务
    好的架构不是设计出来的,而是演进出来的
    缓存穿透
    【转】MySQL数据库主从同步管理
    setup 桌面化设置网卡
    gitlab web登入密码忘记以后可以用如下方式修改密码
    kvm与selinux
    linux下跳板机跟客户端之间无密码登陆
    LINUX下安装TeamViewer
  • 原文地址:https://www.cnblogs.com/qdscwyy/p/9896186.html
Copyright © 2020-2023  润新知