洛谷 P3157 [CQOI2011]動態逆序對 解題報告
阿新 • • 發佈:2018-11-27
P3157 [CQOI2011]動態逆序對
題目描述
對於序列\(A\),它的逆序對數定義為滿足\(i<j\),且\(A_i>A_j\)的數對\((i,j)\)的個數。給\(1\)到\(n\)的一個排列,按照某種順序依次刪除\(m\)個元素,你的任務是在每次刪除一個元素之前統計整個序列的逆序對數。
輸入輸出格式
輸入格式:
輸入第一行包含兩個整數\(n\)和\(m\),即初始元素的個數和刪除的元素個數。以下\(n\)行每行包含一個\(1\)到\(n\)之間的正整數,即初始排列。以下\(m\)行每行一個正整數,依次為每次刪除的元素。
輸出格式:
輸出包含\(m\)行,依次為刪除每個元素之前,逆序對的個數。
說明
\(N\le 100000,M\le 50000\)
萬年以前樹套樹怎麼都是60pts,今天終於決定進行CDQ分治水過去。
每個元素安排三個屬性為\(P_i,A_i,D_i\)分別代表在原序列的位置,元素值和被刪時間。
然後我們統計一下\(P_i < P_j,A_i>A_j,D_i<D_j\)的個數。
然後我調了半個多小時...
終於弄明白\(P_i > P_j,A_i<A_j,D_i<D_j\)也要統計...
Code:
#include <cstdio> #include <algorithm> #define ll long long const int N=1e5+10; struct node{int a,b,p;}sq[N]; int n,m,s[N]; ll ans[N]; void add(int x,int d){while(x<=m)s[x]+=d,x+=x&-x;} int ask(int x){int sum=0;while(x)sum+=s[x],x-=x&-x;return sum;} bool cmp1(node n1,node n2){return n1.a>n2.a;} bool cmp2(node n1,node n2){return n1.a<n2.a;} void CDQ(int l,int r) { if(l==r) return; int mid=l+r>>1; CDQ(l,mid),CDQ(mid+1,r); std::sort(sq+l,sq+r+1,cmp1); for(int i=l;i<=r;i++) { if(sq[i].p<=mid) add(sq[i].b,1); else ans[sq[i].b]+=1ll*(ask(m)-ask(sq[i].b-1)); } for(int i=l;i<=r;i++) if(sq[i].p<=mid) add(sq[i].b,-1); std::sort(sq+l,sq+r+1,cmp2); for(int i=l;i<=r;i++) { if(sq[i].p>mid) add(sq[i].b,1); else ans[sq[i].b]+=1ll*(ask(m)-ask(sq[i].b-1)); } for(int i=l;i<=r;i++) if(sq[i].p>mid) add(sq[i].b,-1); } int main() { scanf("%d%d",&n,&m); for(int a,i=1;i<=n;i++) { scanf("%d",&a); sq[a].a=i; sq[a].b=m; sq[a].p=a; } for(int a,i=1;i<=m;i++) { scanf("%d",&a); sq[a].b=i; } CDQ(1,n); ans[m]>>=1; for(int i=m-1;i;i--) ans[i]+=ans[i+1]; for(int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }
2018.11.27