1. 程式人生 > >P3157 [CQOI2011]動態逆序對(樹狀數組套線段樹)

P3157 [CQOI2011]動態逆序對(樹狀數組套線段樹)

對數 http new -s std pre getchar amp color

P3157 [CQOI2011]動態逆序對

樹狀數組套線段樹

靜態逆序對咋做?樹狀數組(別管歸並QWQ)

然鵝動態的咋做?

我們考慮每次刪除一個元素。

減去的就是與這個元素有關的逆序對數,介個可以預處理:從左到右求一次,再倒過來求一次,用2個數組存起來。

但是前面已經刪除的元素與當前刪除元素組成的逆序對會被重復計數。

於是考慮再減去重復計數

我們用樹狀數組套線段樹(動態開點):

第$i$棵線段樹 儲存 每個位置在$i$之前的被刪除元素

藍後每次查詢時左邊右邊找一找

把它們加回來就好辣

#include<iostream>
#include<cstdio>
#include
<cstring> using namespace std; typedef long long ll; void read(int &x){ static char c=getchar();x=0; while(c<0||c>9) c=getchar(); while(0<=c&&c<=9) x=x*10+(c^48),c=getchar(); } #define W 6000005 #define N 100005 int n,m,u,pos[N],id[N],s[N]; int rt[N],sum[W],lc[W],rc[W]; ll ans,L[N],R[N];
void T_Add(int x,int v){for(;x<=n;x+=x&-x)s[x]+=v;} int T_Sum(int x){int re=0; for(;x;x-=x&-x)re+=s[x]; return re;} #define mid (l+r)/2 void S_Add(int &o,int l,int r,int x){ if(!o) o=++u; ++sum[o]; if(l==r) return; if(x<=mid) S_Add(lc[o],l,mid,x); else S_Add(rc[o],mid+1
,r,x); } int S_Sum(int o,int l,int r,int x1,int x2){ if(x1<=l&&r<=x2) return sum[o]; int re=0; if(x1<=mid) re+=S_Sum(lc[o],l,mid,x1,x2); if(x2>mid) re+=S_Sum(rc[o],mid+1,r,x1,x2); return re; } ll S_Find(int l,int r,int x1,int x2){//查詢l+1~r內所有範圍在x1~x2的個數 if(x1>x2) return 0; ll re=0; for(int i=r;i;i-=i&-i) re+=(ll)S_Sum(rt[i],1,n,x1,x2); for(int i=l;i;i-=i&-i) re-=(ll)S_Sum(rt[i],1,n,x1,x2); return re; } int main(){ read(n);read(m); register int i,j; int q,p; for(i=1;i<=n;++i){ read(pos[i]); id[pos[i]]=i; L[i]=T_Sum(n)-T_Sum(pos[i]); ans+=L[i]; T_Add(pos[i],1); }memset(s,0,sizeof(s)); for(i=n;i;--i) R[i]=T_Sum(pos[i]-1),T_Add(pos[i],1); for(i=1;i<=m;++i){ printf("%lld\n",ans); read(q); p=id[q]; ans-=L[p]+R[p]-S_Find(0,p,q+1,n)-S_Find(p,n,1,q-1); for(j=p;j<=n;j+=j&-j) S_Add(rt[j],1,n,q); }return 0; }

P3157 [CQOI2011]動態逆序對(樹狀數組套線段樹)