• UVA 11990 ``Dynamic'' Inversion (线段树套BIT,分治)


    题目要求可转化为查询一个区间内有多少数比val大(或者小)。

    区间用线段树分解(logN),每个区间维护一rank树。

    rank可用BIT查询,往BIT里面插值,为了保证不同区间的BIT互不影响要先离散。

    首先进行分治,分治的同时归并排序完成离散并计算保存出每个元素和其他元素构成的逆序对iv[i]。(据说这叫归并树

    初始值将所有iv求和,一个对被算了两次所以除以二。

    每次删除元素val就减去val对应的逆序对。

    减去iv[val],但是多减去了和之前删掉元素构成的逆序对(这些逆序对已经算过一次)。

    所以把删掉的元素加到线段树里面。

    减去当前iv[val]之后,查询并加上当前元素val和之前位置以及之后位置构成逆序对。

    复杂度

    O(nlogn)预处理,O(m*logn*logn)回答

    #include<bits/stdc++.h>
    using namespace std;
    
    
    const int maxn = 2e5+5, LogN = 20;
    
    typedef long long ll;
    ll invPair;
    int a[maxn],p[maxn];
    int iv[maxn];
    int n;
    
    int s[maxn<<2];
    int C[LogN][maxn];
    int Set[LogN][maxn];
    
    #define para int o = 1, int l = 1,int r = n,int dep = 0
    #define lo (o<<1)
    #define ro (o<<1|1)
    #define TEMP int mid = (l+r)>>1, lc = lo, rc = ro;
    #define lsn lc, l, mid, dep+1
    #define rsn rc, mid+1, r, dep+1
    
    #define lb(x) ((x)&-(x))
    int sum(int C[],int x)
    {
        int re = 0;
        while(x>0){
            re += C[x];
            x -= lb(x);
        }
        return re;
    }
    
    void add(int C[],int x,int d,int r)
    {
        while(x<=r){
            C[x] += d;
            x += lb(x);
        }
    }
    
    int qpos;
    int ql,qr,val;
    void queryPrefix(para)
    {
        if(1<=l&&r<=qr){
            int pos = upper_bound(Set[dep]+l,Set[dep]+r+1,val)-Set[dep]-l;//等于等于val的最大元素的编号
            invPair += s[o] - sum(C[dep]+l-1,pos);//得到大于val的元素个数
        }else {
            TEMP
            queryPrefix(lsn);
            if(qr>mid) queryPrefix(rsn);
        }
    }
    
    void querySuffix(para)
    {
        if(ql<=l&&r<=n){
            int pos = lower_bound(Set[dep]+l,Set[dep]+r+1,val)-Set[dep]-l;//严格小于val的元素的编号
            invPair += sum(C[dep]+l-1,pos);
        }else {
            TEMP
            if(ql<=mid) querySuffix(lsn);
            querySuffix(rsn);
        }
    }
    
    
    void modify(para)
    {
        s[o]++;
        if(l == r){
            C[dep][l] = 1;
        }else{
            TEMP
            if(qpos<=mid) modify(lsn);
            else modify(rsn);
            int pos = upper_bound(Set[dep]+l,Set[dep]+r+1,val)-Set[dep]-l;//val在set里,从1开始编号
            add(C[dep]+l-1,pos,1,r-l+1);//l-1是0号位,容纳r-l+1个元素
        }
    }
    
    
    //为保证BIT之间互不影响,merge_sort离散,同时计算逆序对
    void discretize(para)
    {
        s[o] = 0;
        memset(C[dep]+l,0,sizeof(int)*(r-l+1));
        if(l == r) {
            Set[dep][l] = a[l];
            return;
        }else {
            TEMP;
            discretize(lsn);
            discretize(rsn);
            int p = l, q = mid+1, k = l;
            while(p<=mid || q<=r){
                if(q > r|| (p <= mid && Set[dep+1][p] <= Set[dep+1][q]) ){
                    iv[Set[dep+1][p]] += k-p;//和后面的数构成逆序对
                    Set[dep][k++] = Set[dep+1][p++];
                }else {
                    iv[Set[dep+1][q]] += mid-p+1;//和前面的数构成逆序对
                    Set[dep][k++] = Set[dep+1][q++];
                }
            }
        }
    }
    
    //#define LOCAL
    int main()
    {
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif
        int m;
        while(~scanf("%d%d",&n,&m)){
            memset(iv+1,0,sizeof(int)*n);
            for(int i = 1; i <= n; i++){
                scanf("%d",a+i);
                p[a[i]] = i;
            }
            invPair = 0;
            discretize();
            for(int i = 1; i <= n; i++){
                invPair += iv[i];
            }
            invPair >>= 1;
            while(m--){
                scanf("%d",&val);
                printf("%lld
    ",invPair);
                invPair -= iv[val];
                qr = p[val]-1;
                ql = p[val]+1;
                if(qr>=1) queryPrefix();
                if(ql<=n) querySuffix();
                qpos = p[val];
                modify();
            }
        }
        return 0;
    }
  • 相关阅读:
    题目1441:人见人爱 A ^ B(二分求幂)
    题目1003:A+B(按逗号分隔的A+B)
    题目1002:Grading(题目背景基于高考打分的简单判断)
    题目1104:整除问题(还是求素数)
    题目1040:Prime Number(第k个素数)
    题目1440:Goldbach's Conjecture(哥达巴赫猜想)
    题目1438:最小公倍数(利用最大公倍数求解)
    题目1439:Least Common Multiple(求m个正数的最小公倍数lcm)
    题目1080:进制转换(任意进制直接转换方法)
    题目1083:特殊乘法(求模运算符的使用)
  • 原文地址:https://www.cnblogs.com/jerryRey/p/4861214.html
Copyright © 2020-2023  润新知