UVA 11990 ``Dynamic'' Inversion

26天以前做过的一道题,之前的做法是分治预处理,树套树在线修改,复杂度为O(nlogn+m*logn*logn),代码量较大。

本来想学习一下cdq分治的,看到论文上的凸包、斜率就暂时放一边了,只知道和一般的分治的不同是左子问题可以用来解决右边的子问题。

今天下午YY了一个离线的分治做法。

对于每一个数字,构成逆序对除了val大小还和被删除的时间del有关,这实际上是一个三维偏序的问题。

一个元素是一个三元组e(pos,val,del),e1和e2对答案有贡献当且仅当e1.pos < e1.pos && e1.val > e2.val && e1.del > e2.del。

第一维pos已经给好了,第二个维度就用归并,而第三个维度就用树状数组(BIT)。

用BIT的想法来源于,BIT插入的顺序对于着之前都已经满足的序,在这里就是已经满足e1.pos < e1.pos && e1.val > e2.val ,

然后用del查询就可以得到满足第三个条件e1.del > e2.del的元素个数。

思想如此,实现上有还值得注意的地方,一是BIT插入的值域范围不能太大,所以我记录了两个关于时间的信息tKth[],tRank[],

tKth[i]表示当前区间第i大的del,tRank[del]表示del在当前区间的名次,(名次从1开始,0表示没有删去),可以很方便地用归并去维护。

二是BIT只能查询小的,需要转化。总对数是容易得到的,用总对数去减就好了。

复杂度依然是O(nlogn+m*logn*logn),但常数很小,代码量也不大。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn = 2e5+5, maxm = 1e5+5;
ll ans;
int iv_pir[maxn], a[maxn], tRank[maxn], tKth[maxn];
int del[maxn], temp[maxn];
int C[2][maxn];
int qry[maxm];

#define lb(x) (x&-x)
void add(int C[],int x,int d,int range)
{
    //if(x<1) return;
    while(x <= range){
        C[x] += d; x += lb(x);
    }
}

int sum(int C[],int x)
{
    int re = 0;
    while(x>0){
        re += C[x]; x -= lb(x);
    }
    return re;
}

void divide(int l,int r)
{
    if(l == r) {
        if(del[a[l]]) {
            tKth[l] = del[a[l]];
        }
        return;
    }
    int mid = (l+r)>>1;
    divide(l, mid);
    divide(mid+1, r);
    //conquer

    int p = l, q = mid+1, k = 0;
    while(p <= mid && !tKth[p]) { temp[k++] = 0; p++; }
    while(q <= r && !tKth[q]) { temp[k++] = 0; q++; }
    int base = l+k-1;
    while(p<=mid || q<=r){
        if(p>mid || (q<=r && tKth[p] > tKth[q])) {
            temp[k++] = tKth[q++];
        }else {
            temp[k++] = tKth[p++];
        }
    }
    memcpy(tKth+l,temp,sizeof(int)*k);
    for(int i = base+1; i <= r; i++){
        tRank[tKth[i]] = i-base;
    }
    int sz = r-base;
    memset(C[0]+1,0,sizeof(int)*(sz));
    memset(C[1]+1,0,sizeof(int)*(sz));
    for(int i = l; i <= mid; i++) {
        if(del[a[i]])
            add(C[0],tRank[del[a[i]]],1,sz);
    }

    p = l, q = mid+1, k = 0;
    while(p<=mid || q<=r){
        if(p>mid || (q<=r && a[p] > a[q])) {
            ans += mid-p+1;
            if(del[a[q]]){
                iv_pir[a[q]] += mid-p+1 - sum(C[0], tRank[del[a[q]]]);
                add(C[1], tRank[del[a[q]]], 1, sz);
            }
            temp[k++] = a[q++];
        }else {
            if(del[a[p]]){
                iv_pir[a[p]] += q-mid-1 - sum(C[1], tRank[del[a[p]]]);
                add(C[0], tRank[del[a[p]]], -1, sz);
            }
            temp[k++] = a[p++];
        }
    }
    memcpy(a+l, temp, sizeof(int)*k);
}

//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    int n, m; ;
    while(~scanf("%d%d", &n, &m)){
        for(int i = 0; i < n; i++) scanf("%d", a+i);
        memset(del+1,0,sizeof(int)*n);
        for(int i = 1; i <= m; i++) {
            scanf("%d", qry+i);
            del[qry[i]] = i;
            iv_pir[qry[i]] = 0;
        }
        ans = 0;
        divide(0,n-1);
        for(int i = 1; i <= m; i++){
            printf("%lld\n", ans);
            ans -= iv_pir[qry[i]];
        }
    }
    return 0;
}

UVA 11990 ``Dynamic'' Inversion

时间: 2024-10-29 03:47:48

UVA 11990 ``Dynamic'' Inversion的相关文章

UVA 11990 `Dynamic&#39;&#39; Inversion CDQ分治, 归并排序, 树状数组, 尺取法, 三偏序统计 难度: 2

题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3141 题意 一个1到n的排列,每次随机删除一个,问删除前的逆序数 思路 综合考虑,对每个数点,令value为值,pos为位置,time为出现时间(总时间-消失时间),明显是统计value1 > value2, pos1 < pos2, time1 < time2的个

UVA 11990 ”Dynamic“ Inversion(线段树+树状数组)

[题目链接] UVA11990 [题目大意] 给出一个数列,每次删去一个数,求一个数删去之前整个数列的逆序对数. [题解] 一开始可以用树状数组统计出现的逆序对数量 对于每个删去的数,我们可以用线段树求出它在原序列中的逆序对贡献 在线段树的每个区间有序化数据,就可以二分查找出这个数在每个区间的位置, 这样就处理出了划分出的区间的贡献,先用答案减去这一部分 接下来考虑已经删去部分的容斥,我们发现只要对删去部分再做一次类似的操作, 将这一部分跟当前删去数求一次贡献就是刚才多减去的部分,将这部分的答案

uva 11990 块状链表

想了想树套树比较难写,于是块链试一下,2秒左右过了. 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 #include <cmath> 6 using namespace std; 7 8 typedef long long ll; 9 const int INF = 9999999; 10 const int N =

UVA 11990(BIT套treap

题目:给出一个1到n的全排列,m个询问,每次删除一个数,输出此时总的逆序对数. 思路:树状数组每个节点都是treap,通过bit套treap来查询每个点前面有多少个比该点小的数... 思路还是比较简单的,但是写了挺长时间,现在一个很严重的缺点就是代码量一大就没有定力,然后直接gg.....以后要多写代码题尽力克服这个问题... /* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000")

挑战程序设计竞赛 3.3 活用各种数据结构

[Summarize] 1. 线性维护只能处理部分问题的时候要想到数据拆分,容斥解决问题 2. 在不断有人被淘汰的序列问题中,查找左右第几个人是谁的时候可以考虑线段树优化 3. 当问题结果并不同时依赖与两个维度的操作的时候,二维问题可拆分为两个一维问题分别解决 POJ 1990:MooFest /* 题目大意:给出每头奶牛的位置和至少要多少分贝的音量才能听到谈话 现在求奶牛两两交流成功需要的距离*分贝的总和. 题解:我们将奶牛对于需要的分贝排序,那么在计算的时候, 每头奶牛只要计算和序列前面所有

CDQ

左边为原oj的链接,右边为VJ上的链接 入门: CodeForces-669E Little Artem and Time Machine   题解 CodeForces-97B Superset Bzoj-3110  K大数查询 Bzoj-2001 City 城市建设    题解 Bzoj-4553 序列 UVA-11990 "Dynamic'' Inversion HDU-4742 Pinball Game 3D UVA-6667 Longest Chain   题解 HDU-5618 Ja

(待修莫队 没过! 抽空在检查)Dynamic len(set(a[L:R])) UVA - 12345

#include <iostream> #include <cstdio> #include <sstream> #include <cstring> #include <map> #include <set> #include <vector> #include <stack> #include <queue> #include <algorithm> #include <cma

UVA 10817 Headmaster&#39;s Headache 状压DP

记录两个状态S1,S2分别记录哪些课程被1个人教过或2个人教过,然后记忆化搜索 UVA - 10817 Headmaster's Headache Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description Problem D: Headmaster's Headache Time limit: 2 seconds The headmaster of Spr

UVA 108 Maximum Sum

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&page=show_problem&problem=44 Dynamic Programming. 对所有可能存在的长方形计算sum,利用已知的小的sub-rectangle计算包含有这些相减或相加得到的new rectangle.代码如下: 1 #include <iostream> 2 #