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\n", res);
    return 0;
}
posted @ 2018-11-02 14:42  Grary  阅读(213)  评论(0编辑  收藏  举报
博客园 首页 私信博主 编辑 关注 管理 新世界