Luo Gu P3157 dynamic reverse order

思路就是对于每一个删除的数,统计在它之前有多少比它大,在它之后有多少比它小。

然后直接想到树套树(实际上是想不到更好的方法),然而树套树极其难写,所以发一个归并树的题解吧。

归并树是对于线段树每一个节点,维护一个从L到R的有序数组(一般用vector实现防止MLE),有点像归并排序的过程。

对于删除操作,我们沿着线段树的查询路径,一路更改遇到的vector,删除对应的数(用二分查找实现是logn),所以一次修改的消耗是log^2(n)。

查询就是线段树的查询方式,把询问区间拆成若干互不相交的子区间,在每个区间内二分查找,然后累加。
PS:因为归并树玄学的常数问题,需要开O2
#include<cstdio>
#include<cstdlib>
#include<vector>
#define maxn 100010
#include<algorithm>
#define ll long long
#define IT vector<int>::iterator
using namespace std;
struct node{
    int l,r;
    vector<int> v;
} tree[maxn<<2];
int n,m,a[maxn],c[maxn],p[maxn],l1[maxn],l2[maxn];
int lowbit(int x){
    return x&(-x);
}
void change(int x){
    while(x<=n){
        c[x]++;
        x+=lowbit(x);
    }
}
int query(int x){
    int ans=0;
    while(x){
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}
void build(int pos,int left,int right){
    int mid=left+right>>1;
    int CNT1 = 0 , CNT2 = 0 ;
     // standard recursion segment tree 
    Tree [POS] .L = left; Tree [POS] .r = right;
     IF (left == right) { 
        Tree [POS] .v.push_back ( a [left]); 
        return ; 
    } 
    Build (POS << . 1 , left, MID); 
    Build (POS << . 1 | . 1 , MID + . 1 , right);
     // the ordered array merge together the left and right child nodes 
    for ( I = the IT Tree Register [<< POS . 1 ] .v.begin (); I = Tree [POS <<! . 1 ] .v.end (); I ++ ) 
        L1 [ ++ CNT1] = *i;
    for(register IT i=tree[pos<<1|1].v.begin();i!=tree[pos<<1|1].v.end();i++)
        l2[++cnt2]=*i;
    int i=1,j=1;
    while(i<=cnt1&&j<=cnt2){
        if(l1[i]<l2[j]) tree[pos].v.push_back(l1[i++]);
        else tree[pos].v.push_back(l2[j++]);
    }
    while(i<=cnt1) tree[pos].v.push_back(l1[i++]);
    while(j<=cnt2) tree[pos].v.push_back(l2[j++]);
    return;
}
void modify(int pos,int tar){
    int mid=tree[pos].l+tree[pos].r>>1;
    IT it=lower_bound(tree[pos].v.begin(),tree[pos].v.end(),a[tar]);//找到目标数位置 
    tree[pos].v.erase(it);//删除 
    if(tree[pos].l==tree[pos].r) return;
    if(tar<=mid) modify(pos<<1,tar);
    else modify(pos<<1|1,tar);
    return;
}
int ask(int pos,intleft, int right, int key, int of the type) { // of the type said to be smaller than the key statistics or greater than the key of 
    IT IT;
     int ANS = 0 ;
     // if the current interval is included in the inquiry interval, two assigned to the first greater than key position 
    IF (Tree [POS] .L> left = && Tree [POS] .r <= right) { 
        IT = upper_bound, (Tree [POS] .v.begin (), Tree [POS] .v.end () , Key); // note is upper_bound, 
        IF (type == . 1 ) return iT-Tree [POS] .v.begin (); // count less than its behind 
        the else  return Tree [POS] .v.end ( ) -it; // in front of the statistics is bigger than its 
    }
     //递归查左右子树 
    int mid=tree[pos].l+tree[pos].r>>1;
    if(left<=mid) ans+=ask(pos<<1,left,right,key,type);
    if(right>mid) ans+=ask(pos<<1|1,left,right,key,type);
    return ans;
}
int main(){
    int x;
    ll tot=0;
    //标准读入 
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        p[a[i]]=i;
    }
    //树状数组处理初始逆序对 
    for(register int i=n;i>=1;i--){
        change(a[i]);
        tot+=query(a[i]-1);
    }
    build(1,1,n);
    printf("%lld\n",tot);
    for(register int i=1;i<m;i++){
        scanf("%d",&x);
        Modify ( . 1 , P [X]); // delete 
        tot- ASK = ( . 1 , P [X] + . 1 , n-, X, . 1 ); // subtracting the contribution of the front and rear two portions 
        tot- ASK = ( . 1 , . 1 , P [X] - . 1 , X, 2 ); 
        the printf ( " % LLD \ n- " , TOT); 
    } 
    Scanf ( " % D " , & X);
     return  0 ; 
}

 

 

Guess you like

Origin www.cnblogs.com/landmine-sweeper/p/10959018.html