標籤:href 數列 memset [] http printf return 開始 操作
【題目連結】 UVA11990
【題目大意】
給出一個數列,每次刪去一個數,求一個數刪去之前整個數列的逆序對數。
【題解】
一開始可以用樹狀數組統計出現的逆序對數量
對於每個刪去的數,我們可以用線段樹求出它在原序列中的逆序對貢獻
線上段樹的每個區間有序化資料,就可以二分尋找出這個數在每個區間的位置,
這樣就處理出了劃分出的區間的貢獻,先用答案減去這一部分
接下來考慮已經刪去部分的容斥,我們發現只要對刪去部分再做一次類似的操作,
將這一部分跟當前刪去數求一次貢獻就是剛才多減去的部分,將這部分的答案再加回去。
這個可以線上段樹上尋找的同時用樹狀數組維護。
這樣子就能處理每一次的刪數操作了。
【代碼】
#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int N=200010;int n,m,c[30][N],a[30][N],arr[N],id[N];long long ans;void add(int c[],int x,int v,int n){while(x<=n)c[x]+=v,x+=x&-x;}int sum(int c[],int x,int n=0){int s=0;while(x>n)s+=c[x],x-=x&-x;return s;}void build(int x,int l,int r,int d){ int mid=(l+r)>>1; for(int i=l;i<=r;i++)a[d][i]=a[d-1][i],c[d][i]=0; if(l==r)return; build(x<<1,l,mid,d+1); build(x<<1|1,mid+1,r,d+1); sort(a[d]+l,a[d]+r+1);}int find(int L,int R,int d,int v){ int l=L,r=R; while(l<r){ int mid=(l+r)>>1; if(a[d][mid]>=v)r=mid; else l=mid+1; }if(a[d][l]>v)l--; return l;}void query(int x,int l,int r,int L,int R,int v,int d,int f){ int mid=(l+r)>>1; if(L<=l&&r<=R){ int k=find(l,r,d,v),t=sum(c[d],k,l-1); if(!f){k=r-k;t=sum(c[d],r,l-1)-t;} else k-=l-1; ans-=k-t; return; }if(l>=r)return; if(L<=mid)query(x<<1,l,mid,L,R,v,d+1,f); if(R>mid)query(x<<1|1,mid+1,r,L,R,v,d+1,f);}void update(int x,int l,int r,int s,int v,int d){ int mid=(l+r)>>1; if(l==r){add(c[d],l,1,r);return;} if(l>=r)return; if(s<=mid)update(x<<1,l,mid,s,v,d+1); else update(x<<1|1,mid+1,r,s,v,d+1); int k=find(l,r,d,v); add(c[d],k,1,r);}int main(){ while(~scanf("%d%d",&n,&m)){ ans=0; memset(arr,0,sizeof(arr)); for(int i=1;i<=n;i++){ scanf("%d",&a[0][i]); id[a[0][i]]=i; ans+=i-1-sum(arr,a[0][i]); add(arr,a[0][i],1,n); }build(1,1,n,1); while(m--){ int k; scanf("%d",&k); printf("%lld\n",ans); if(ans){ query(1,1,n,1,id[k]-1,k,1,0); query(1,1,n,id[k]+1,n,k,1,1); update(1,1,n,id[k],k,1); } } }return 0;}
UVA 11990 ”Dynamic“ Inversion(線段樹+樹狀數組)