BZOJ3295 [Cqoi2011]動態逆序對


本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!

 

Description

對於序列A,它的逆序對數定義為滿足i<j,且Ai>Aj的數對(i,j)的個數。給1到n的一個排列,按照某種順序依次刪除m個元素,你的任務是在每次刪除一個元素之前統計整個序列的逆序對數。

Input

輸入第一行包含兩個整數nm,即初始元素的個數和刪除的元素個數。以下n行每行包含一個1到n之間的正整數,即初始排列。以下m行每行一個正整數,依次為每次刪除的元素。
 

Output

輸出包含m行,依次為刪除每個元素之前,逆序對的個數。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

樣例解釋
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

 

N<=100000 M<=50000

 
 
正解:CDQ分治
解題報告:
 
  CDQ分治裸題。其實也是樹套樹裸題,那么拿來當CDQ練手吧。
  考慮把刪除變成倒着插入,那么我給每個坐標一個權值t,表示插入時間。那么第一個刪除的t坐標當然是n,表示最后一個插入。然后為了方便,我們把未被刪除的結點的t坐標從左往右設為1、2、3...
  考慮問題轉換成了求對於(t0,x0,y0)滿足t<t0,x<x0,y>y0的(t,x,y)的個數,這樣就變成了三維偏序的裸題了。細節上有必要再說一下:
  首先CDQ分治之前按t排序,保證t已經有序,在每次分治內部,按x排序,正着掃整個區間的時候,對於[mid+1,r]的區間就在樹狀數組上查詢大於他的y的值的數量;倒着掃,對於[mid+1,r]的區間就在樹狀數組上查詢小於他的y的值的數量。因為左邊的所有元素對於右邊的所有元素而言,是可以肯定t要小一些的,所以CDQ分治就可以巧妙地解決三維偏序的問題。之后在遞歸處理左邊右邊就可以了,同樣的做法。
  ps:我開始T了兩發,犯的是寫CDQ分治的常見錯誤,就是在分治里面清空了數組,事實上只要清除剛才打上去的標記就可以了,無需清空。
 
 
 1 //It is made by ljh2000
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 using namespace std;
14 typedef long long LL;
15 const int inf = (1<<30);
16 const int MAXN = 100011;
17 int n,m,c[MAXN],match[MAXN],ans[MAXN];
18 LL Ans;
19 struct node{
20     int x,y,t;
21     int flag;
22 }a[MAXN],b[MAXN];
23 inline bool cmpx(node q,node qq){ if(q.x==qq.x) return q.y<qq.y; return q.x<qq.x; }
24 inline bool cmpt(node q,node qq){ return q.t<qq.t; }
25 inline void add(int x,int val){ while(x<=n) c[x]+=val,x+=x&(-x); }
26 inline int query(int x){int tot=0; while(x>0) tot+=c[x],x-=x&(-x); return tot;  }
27 inline int getint()
28 {
29     int w=0,q=0; char c=getchar();
30     while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
31     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
32 }
33 
34 inline void CDQ(int l,int r){
35     if(l>=r) return ; int mid=(l+r)>>1,size=r-l+1,cnt=0;
36     for(int i=l;i<=mid;i++) b[++cnt]=a[i],b[cnt].flag=0; for(int i=mid+1;i<=r;i++) b[++cnt]=a[i],b[cnt].flag=1;
37     sort(b+1,b+cnt+1,cmpx); //for(int i=1;i<=n;i++) c[i]=0;
38     for(int i=1;i<=size;i++) {
39     if(b[i].flag==0) add(b[i].y,1);
40     else ans[b[i].t]+=query(n)-query(b[i].y);
41     }
42     for(int i=1;i<=size;i++) if(b[i].flag==0) add(b[i].y,-1);
43     for(int i=size;i>=1;i--) {
44     if(b[i].flag==0) add(b[i].y,1);
45     else ans[b[i].t]+=query(b[i].y);
46     }
47     for(int i=1;i<=size;i++) if(b[i].flag==0) add(b[i].y,-1);
48     CDQ(l,mid); if(mid<r) CDQ(mid+1,r);
49 }
50 
51 inline void work(){
52     n=getint(); m=getint(); for(int i=1;i<=n;i++) { a[i].x=i; a[i].y=getint(); match[a[i].y]=i; } int cc=n,x;
53     for(int i=1;i<=m;i++) { x=getint(); a[match[x]].t=cc--; }  for(int i=1;i<=n;i++) if(a[i].t==0) a[i].t=cc--;
54     sort(a+1,a+n+1,cmpt); CDQ(1,n);
55     for(int i=1;i<=n;i++) Ans+=ans[i];
56     for(int i=n;i>n-m;i--) {
57     printf("%lld\n",Ans);
58     Ans-=ans[i];
59     }
60 }
61 
62 int main()
63 {
64     work();
65     return 0;
66 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM