• P3157 [CQOI2011]动态逆序对(CDQ分治)


    题目描述

    对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

    输入格式

    输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。

    输出格式

    输出包含m行,依次为删除每个元素之前,逆序对的个数。

    输入输出样例

    输入 #1
    5 4
    1
    5
    3
    4
    2
    5
    1
    4
    2
    输出 #1
    5
    2
    2
    1
    
    样例解释
    (1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

    说明/提示

    N<=100000 M<=50000

    题解: 对于一个逆序对,加一个时间维,越往后删除的时间戳越小,没有删除的值的时间戳为0,这题就变成了一个三维偏序.

    我们把时间看作第一维, 从小到大排序, (j,i)作为一个逆序对出现, 则j的时间戳必定小于i的时间戳.

    那么有两种情况, Tj < Ti, Pj < Pi, Vj > Vi        or     Tj < Ti, Pj > Pi, Vj < Vi  ( T代表时间,P代表位置,V代表值)

    分情况处理即可.

    由于我们处理的是每个时间戳所增加的逆序对,所以我们要求一个前缀和,然后倒序输出即可.

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 200010;
    ll c[maxn];
    int n,m;
    int lowbit(int x){return x & (-x);}
    void add(int pos,int x){
        for(int i = pos;i <= n;i += lowbit(i)){
            c[i] += x;
        }
    }
    ll query(int pos){
        ll sum = 0;
        for(int i = pos; i >= 1;i -= lowbit(i)){
            sum += c[i];
        }
        return sum;
    }
    struct node{
        int tim,pos,val;
    }a[maxn];
    int cmp1(node a,node b){//按照时间排序
        if(a.tim == b.tim)return a.pos < b.pos;
        return a.tim < b.tim;
    }
    int cmp2(node a,node b){//按照位置从小到大排序
        return a.pos < b.pos;
    }
    int cmp3(node a,node b){//按照位置从大到小排序
        return a.pos > b.pos;
    }
    ll pos[maxn],ans[maxn];
    void cdq(int l,int r){
        if(l == r)return ;
        int mid = (l + r) / 2;
        cdq(l,mid),cdq(mid+1,r);
        int i = mid + 1,j = l;//这里按照第二维 位置从小到大排序,对于右边区间的每一个点i,查询满足 pos(j) < pos(i) && val(j) > val(i)的j有多少个
        sort(a + l,a + mid + 1,cmp2);
        sort(a + mid + 1,a + r + 1,cmp2);
        for(;i <= r;i++){
            while(a[j].pos <= a[i].pos && j <= mid){
                add(a[j].val,1);
                j++;
            }
            ans[a[i].tim] += query(n) - query(a[i].val);
        }
        for(int i = l;i < j;i++)add(a[i].val,-1);//清空树状数组
        i = mid + 1,j = l;//这里按照第二维 位置从大到小排序,对于右边区间的每一个点i,查询满足 pos(j) > pos(i) && val(j) < val(i)的j有多少个
        sort(a + l,a + mid + 1,cmp3);
        sort(a + mid + 1,a + r + 1,cmp3);
        for(;i <= r;i++){
            while(a[j].pos >= a[i].pos && j <= mid){
                add(a[j].val,1);
                j++;
            }
            ans[a[i].tim] += query(a[i].val);
        }
        for(int i = l;i < j;i++)add(a[i].val,-1);//清空树状数组
    }
    int main()
    {
        cin >> n >> m;
        for(int i = 1;i <= n;i++){
            cin >> a[i].val;
            a[i].tim = 0;
            pos[a[i].val] = i;//记录每个值的位置
            a[i].pos = i;
        }
        for(int i = 1;i <= m;i++){
            int x;
            cin >> x;
            a[pos[x]].tim = m - i + 1;//时间戳更新
        }
        sort(a+1,a+1+n,cmp1);
        cdq(1,n);
        for(int i = 1;i <= m;i++)ans[i] += ans[i-1];
        for(int i = m;i >= 1;i--)cout << ans[i] << endl;
        return 0;
    }
    View Code
  • 相关阅读:
    jquery ajax 返回数据时 ff正常,ie接受到数据但是显示不了
    查看IIS日志并且分析其中的错误日志
    用eventvwr查看系统日志
    C++实现Trie 树
    [算法之美笔记02] 栈模拟网页的前进后退 ; 阻塞队列与并发队列
    MySQL学习小记(三) 结合JDBC实现用户的登录响应
    [算法之美笔记01] 数组,链表的删除和垃圾回收,缓存机制有什么关系
    [埋坑系列] 基于QT/C++的杰瑞走迷宫小游戏 :1.大体构造
    品味C++实现AVL树的删除操作
    C++实现AVL树的四种旋转
  • 原文地址:https://www.cnblogs.com/cherish-lin/p/11963823.html
Copyright © 2020-2023  润新知