動態逆序對-CQOI2011
[button color="info" icon="" url="https://www.luogu.com.cn/problem/P3157" type=""]題目傳送門[/button]
Problem
有一個長度為n的1~n的排列,現在進行m次刪除操作,每次給定刪除的元素,要求在每次刪除之前輸出序列裡的逆序對個數。
Solution
考慮最普通的靜態逆序對,顯然可以用樹狀陣列來維護,那麼每次刪除一個數p,逆序對個數減少的就是(p前面大於p的數)和(p後面小於p的數),立馬就有一個樸素的想法:在樹狀數組裡修改刪除,但是馬上也就能把這個想法切掉,因為刪除操作開始後,所有數字已經加入進去了,此時的統計是沒有意義的。
那麼我們就要考慮如何完成這個維護這個順序的問題,另一個想法就是用主席樹套樹狀陣列,但是我不會,所以讓我們來想想其他的辦法。
然後就考慮分塊。前面的塊裡的元素無論如何,一定在後面塊裡元素的前面。所以我們就考慮在刪除的元素的塊內暴力,其他的塊內直接利用前後關係來計算對答案的貢獻。
經過實踐,發現分100塊左右是最優的,就算卡滿時間複雜度也只有O(1000*m),大概5e7的時間複雜度,顯然是能卡過去的。
具體操作就是對每一塊需要查詢比某個元素x大或小的個數,那麼每一塊開兩個樹狀陣列就可以實現了,第一個在1處插1,a[i]處插-1,這樣我們ask(x)得到的就是大於它的元素個數,第二個在a[i]處插1,ask(x)得到是小於它的元素個數。
至於上面說的暴力是怎麼暴力呢?真就列舉原數列判斷p前面比它大的,p後面比它小的,這個操作是O(塊長)的,上面那個整塊的操作是O(塊數logn)的,logn大致是17,整體是差不多在O(1000)這樣一個級別,所以時間複雜度是正確的。
程式碼實現也挺簡單的。。至少比機房那個打主席樹套樹狀陣列還沒調出來的簡單。
#include<bits/stdc++.h> #define reg register #define int long long #define len 1000 #define L(x) ((x)*len-len+1) #define R(x) ((x)*len) #define lowbit(x) (x&-x) using namespace std; const int N=1e5+10,SN=110; int a[N],num[N],c[SN][N][2]; int to[N],wz[N],vis[N],n,m; inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } inline void add(int u,int v,int pos,int p){ for(reg int i=u;i<=n;i+=lowbit(i)) c[pos][i][p]+=v; } inline int ask(int u,int pos,int p){ int cnt(0); for(reg int i=u;i;i-=lowbit(i)) cnt+=c[pos][i][p]; return cnt; } inline void radd(int u,int v,int pos){ add(u,v,pos,0),add(1,v,pos,1),add(u,-v,pos,1); } signed main(){ cerr<<"len:"<<len<<endl; n=read(),m=read(); int ans=0; for(reg int i=1;i<=n;i++){ a[i]=read(); ans+=ask(a[i],0,1);radd(a[i],1,0); } for(reg int i=1;i<=n;i++) num[i]=(i-1)/len+1,to[a[i]]=num[i],wz[a[i]]=i,radd(a[i],1,num[i]); while(m--){ reg int p(read()),b=to[p],w=wz[p]; printf("%lld\n",ans); radd(p,-1,b);vis[p]=1; for(reg int i=L(b),e=R(b);i<=e&&i<=n;i++) if(((i<w&&a[i]>a[w])||(i>w&&a[i]<a[w]))&&!vis[a[i]])ans--; for(reg int i=num[1],e=num[n];i<=e;i++){ if(i<b)ans-=ask(p,i,1); if(i>b)ans-=ask(p,i,0); } } return 0; }