• [BZOJ3295] [Cqoi2011]动态逆序对(带修改主席树)


    题目描述

    对于序列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

    题解

    原来还以为自己已经会带修改主席树了呢……才发现自己还是太naive……

    然后找到的题解全是CDQ分治的……我这个蒟蒻有点方……

    然后发现还是zcysky大佬写的最吼啦

    还是来详细的说一说

    先考虑无修改的逆序对怎么做?

    很明显,用树状数组(虽然我今天之前一直以为逆序对个数只能用归并做)

    我们记$a1[i]$表示在$i$之前且比$i$大的数的个数(注意,这里的i指的是位置),那么很明显答案为$sum _{i=1}^n a[i]$

    代码实现

    1 for(int i=1;i<=n;++i){
    2     val[i]=read(),pos[val[i]]=i;
    3     a1[i]=ask(n)-ask(val[i]);
    4     ans+=a1[i];
    5     for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j];
    6 }

    记$a2[i]$表示在$i$之后且比$i$小的数的个数,只要把上面那个倒着推就行了

    1 for(int i=n;i;--i){
    2     a2[i]=ask(val[i]-1);
    3     for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j];
    4 }

    接下来我们考虑修改操作。

    每一次将一个数删除,减少的逆序对个数是多少?

    很明显是$a1[i]+a2[i]$,然后我们就可以做啦

    于是评测机表示并不想理你并丢给你一堆WA

    这个时候我们发现自己忽略了一个关键的问题,如果$a1[i]$和$a2[i]$中表示的数已经有被删除了的怎么办?

    我们只要把这些被删除的数减去即可

    具体来说,我们可以考虑用一个带修改主席树维护

    因为主席树维护的是前缀和

    如果按照一般思想,一个一个去更改太浪费时间了

    我们想到,前缀和可以用树状数组的思想来维护

    于是我们可以用树状数组的思想建主席树

    于是每一次更改就可以减少到做$log n$次了

    所以每一次删去一个数,我们就在主席树上插入这个数

    要算答案时,只要减去$a1[i]$和$a2[i]$,再把删除的数加回来就好了

    只要在主席树上$[1,i-1]$区间中大于$val[i]$的数的个数和$[i+1,n]$区间中小于$val[i]$的数的个数即可

      1 //minamoto
      2 #include<bits/stdc++.h>
      3 #define N 100005
      4 #define M 5000005
      5 #define ll long long
      6 using namespace std;
      7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
      8 char buf[1<<21],*p1=buf,*p2=buf;
      9 inline ll read(){
     10     #define num ch-'0'
     11     char ch;bool flag=0;ll res;
     12     while(!isdigit(ch=getc()))
     13     (ch=='-')&&(flag=true);
     14     for(res=num;isdigit(ch=getc());res=res*10+num);
     15     (flag)&&(res=-res);
     16     #undef num
     17     return res;
     18 }
     19 char obuf[1<<24],*o=obuf;
     20 void print(ll x){
     21     if(x>9) print(x/10);
     22     *o++=x%10+48;
     23 }
     24 int L[M],R[M],sum[M],rt[N];
     25 int val[N],pos[N],xx[N],yy[N],c[N],a1[N],a2[N];
     26 int n,cnt,q;ll ans=0;
     27 inline int lowbit(int x){return x&(-x);}
     28 int ask(int x){
     29     int s=0;
     30     for(int i=x;i;i-=lowbit(i)) s+=c[i];
     31     return s;
     32 }
     33 void update(int &now,int l,int r,int k){
     34     if(!now) now=++cnt;
     35     ++sum[now];
     36     if(l==r) return;
     37     int mid=(l+r)>>1;
     38     if(k<=mid) update(L[now],l,mid,k);
     39     else update(R[now],mid+1,r,k);
     40 }
     41 int querysub(int x,int y,int v){
     42     int cntx=0,cnty=0,ans=0;--x;
     43     for(int i=x;i;i-=lowbit(i)) xx[++cntx]=rt[i];
     44     for(int i=y;i;i-=lowbit(i)) yy[++cnty]=rt[i];
     45     int l=1,r=n;
     46     while(l<r){
     47         int mid=(l+r)>>1;
     48         if(v<=mid){
     49             for(int i=1;i<=cntx;++i) ans-=sum[R[xx[i]]];
     50             for(int i=1;i<=cnty;++i) ans+=sum[R[yy[i]]];
     51             for(int i=1;i<=cntx;++i) xx[i]=L[xx[i]];
     52             for(int i=1;i<=cnty;++i) yy[i]=L[yy[i]];
     53             r=mid;
     54         }
     55         else{
     56             for(int i=1;i<=cntx;++i) xx[i]=R[xx[i]];
     57             for(int i=1;i<=cnty;++i) yy[i]=R[yy[i]];
     58             l=mid+1;
     59         }
     60     }
     61     return ans;
     62 }
     63 int querypre(int x,int y,int v){
     64     int cntx=0,cnty=0,ans=0;--x;
     65     for(int i=x;i;i-=lowbit(i)) xx[++cntx]=rt[i];
     66     for(int i=y;i;i-=lowbit(i)) yy[++cnty]=rt[i];
     67     int l=1,r=n;
     68     while(l<r){
     69         int mid=(l+r)>>1;
     70         if(v>mid){
     71             for(int i=1;i<=cntx;++i) ans-=sum[L[xx[i]]];
     72             for(int i=1;i<=cnty;++i) ans+=sum[L[yy[i]]];
     73             for(int i=1;i<=cntx;++i) xx[i]=R[xx[i]];
     74             for(int i=1;i<=cnty;++i) yy[i]=R[yy[i]];
     75             l=mid+1;
     76         }
     77         else{
     78             for(int i=1;i<=cntx;++i) xx[i]=L[xx[i]];
     79             for(int i=1;i<=cnty;++i) yy[i]=L[yy[i]];
     80             r=mid;
     81         }
     82     }
     83     return ans;
     84 }
     85 int main(){
     86     //freopen("testdata.in","r",stdin);
     87     n=read(),q=read();
     88     for(int i=1;i<=n;++i){
     89         val[i]=read(),pos[val[i]]=i;
     90         a1[i]=ask(n)-ask(val[i]);
     91         ans+=a1[i];
     92         for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j];
     93     }
     94     memset(c,0,sizeof(c));
     95     for(int i=n;i;--i){
     96         a2[i]=ask(val[i]-1);
     97         for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j];
     98     }
     99     while(q--){
    100         print(ans),*o++='
    ';
    101         int x=read();x=pos[x];
    102         ans-=(a1[x]+a2[x]-querysub(1,x-1,val[x])-querypre(x+1,n,val[x]));
    103         for(int j=x;j<=n;j+=lowbit(j)) update(rt[j],1,n,val[x]);
    104     }
    105     fwrite(obuf,o-obuf,1,stdout);
    106     return 0;
    107 }
  • 相关阅读:
    JS 跨域问题。。
    LInq 中使用正则表达试
    CreateXMl
    DeleteXMl
    SameNameFile 比较两个文件夹是否同名
    UpdateXML
    AddXML
    AsDataView Dataview ,DataTable 跟linq的相互转化
    AttributeToElement
    WoreTime 计算单词出现的次数
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9396076.html
Copyright © 2020-2023  润新知