1. 程式人生 > >最簡單的問題(重慶市第八屆大學生程序設計大賽D) (線段樹+離線思想)

最簡單的問題(重慶市第八屆大學生程序設計大賽D) (線段樹+離線思想)

return ans img 個數 pre 子序列 clear 可持久化 sort

技術分享圖片

考場上的時候直接一臉懵逼了,啥? 區間裏面又要求子區間,還TM有上下界?

略加思索後倒是發現沒有那麽麻煩,因為很容易得出如下結論:

1.對於一個滿足條件的區間[L , R],對於他所有的子區間顯然也滿足條件。

2.一個區間[L , R]的子區間數量(包括自己)為(R-L+1)*(R-L+2)/2

 證:因為區間都是連續的,所以顯然該區間含有R-L+1個長度為1的子區間,R-L個長度為2的子區間,R-L-1個長度為3的子區間......1個長度為R-L+1的子區間

然後根據等差數列求和公式 Sn = n * (n + 1) / 2 就算出來了

根據上面的結論,不考慮子區間裏面的子區間,顯然[L , R]裏面有一段或者若幹段最大值在[l , r]裏面的子區間 比如說樣例吧 就有[1 3] [5 5]兩段,然後把這兩段分別按照上面的公式計算並相加 即 ( 3 * 4 / 2 ) + ( 1 * 2 / 2 ) = 6 + 1 = 7

但這感覺還是不好處理啊,想了半天可持久化然後發現並無卯月orz...

賽後才知道是偉大的離線思想....果然我把學的東西全都還給HB了 TAT..

為了方便理解,這裏有某道比較裸的離線線段樹

https://www.cnblogs.com/H-Vking/p/4296171.html

然後回歸這道題,因為這題只是查詢不存在修改,然後就可以離線了

建一棵空線段樹,然後把每個數據b [ i ]和詢問a [ i ]存下來,然後再把b從小到大排序,再裏面把a按照上界r排序,然後因為r是單調上升的,我們就可以把所有小於當前詢問的r,及所有滿足 b [ j ] <= a [ i ] . r 的b [ j ] 全部加進線段樹裏面去,顯然,線段樹裏面有些數不存在,於是整個序列就被分成了一段或若幹段,我們就可以通過上述方法查詢[L , R]裏面子區間的個數就行了,這種離線操作就完美保證了查詢的時候查到的絕對是小於r的,因為大於r的都沒有加進線段樹裏面....

註意,我們這裏線段樹的查詢操作查詢的就應該是子區間個數了,因為根本不可能把那些滿足條件的段全部記錄下來嘛(其實是因為我太菜了不會這樣維護orz)

然後我們在線段樹裏面維護如下數據:

1. 該段區間的子區間個數之和ans[]

2. 該段區間包含序列的數的個數sz[]

3. 該段區間從最左邊第一個數往右數的連續子序列長度ls[]

4. 該段區間從最右邊第一個數往左數的連續子序列長度rs[]

ls,rs和sz的維護很明顯(註意當左兒子sz等於ls的時候還要加右兒子的ls,右邊同理),那麽ans怎麽維護呢?

其實也很明顯,ans [now] =ans [lc] + ans [rc] + calc ( rs [lc] + ls [rc] ) - calc ( rs [lc] ) - calc ( ls [rc] )

(calc(x)為上面計算子區間的公式函數)

原因就是左邊rs區間和右邊ls區間合成了一個新區間,然後我們要做的就是把之前的子區間個數加上合成的新區間的子區間個數再減去兩個原區間的子區間個數

似乎看起來會漏算,但根據偉大的分治思想實際上並不會(覺得好像有問題的可以自己模擬一下,就知道為什麽可以了)

然後我們發現經過對[L , R]的查詢所返回的ans就是答案,於是這個問題就愉快地被解決掉了(其實並不愉快orz)

什麽?你說下界怎麽辦?那其實我們可以用前綴和的思想,對於每個詢問,我們把他拆成兩個詢問,就是把詢問[l , r]拆開成[1 , r] 和 [1 , l-1],顯然,該查詢的答案就是[1 , r]的查詢結果減去[1 , l-1]的查詢結果。

所以說每輸入一個詢問,我們就把他拆成兩個只存在上界的詢問,上界分別是l-1和r,然後把上界是l-1的詢問標記一下,查詢的時候判斷這個詢問是在做減法就行了

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #define LL long long
  6 using namespace std;
  7 const int maxn=1e5+5;
  8 int np=0,rt;
  9 int n,m,T;
 10 struct node//線段樹維護的值 
 11 {
 12     int sz;
 13     LL ls,rs,ans;
 14     node()
 15     {
 16         sz=ls=rs=ans=0;
 17     }
 18 }w[maxn];
 19 int lc[maxn],rc[maxn];
 20 LL ANS[maxn];
 21 
 22 struct opt//序列的值 
 23 {
 24     int pos,w;
 25     friend bool operator <(opt a,opt b)
 26     {
 27         return a.w<b.w;
 28     }
 29 }b[maxn];
 30 
 31 struct data//詢問 
 32 {
 33     int id,tot;
 34     int r;
 35     int L,R;
 36     friend bool operator <(data a,data b)
 37     {
 38         return a.r<b.r;
 39     }
 40 }a[maxn];
 41 
 42 LL calc(LL x){return x*(x-1)/2;}
 43 
 44 node update(node a,node b)//維護 
 45 {
 46     node c;
 47     
 48     c.sz=a.sz+b.sz;
 49     c.ls=a.ls,c.rs=b.rs;
 50     
 51     if(a.sz==a.ls) c.ls+=b.ls;
 52     if(b.sz==b.rs) c.rs+=a.rs;
 53     
 54     c.ans=a.ans+b.ans+calc(a.rs+b.ls)-calc(a.rs)-calc(b.ls);
 55     
 56     return c;
 57 }
 58 
 59 void pushup(int x)//上傳 
 60 {
 61     w[x]=update(w[lc[x]],w[rc[x]]);
 62     return;
 63 }
 64 
 65 void build(int &now,int L,int R)
 66 {
 67     now=++np;
 68     if(L==R) {w[now].sz=1;return;}
 69     int m=L+R>>1;
 70     build(lc[now],L,m);
 71     build(rc[now],m+1,R);
 72     w[now].sz=w[lc[now]].sz+w[rc[now]].sz;
 73     return;
 74 }
 75 void modify(int now,int L,int R,int x)
 76 {
 77     if(L==R)
 78     {
 79         w[now].ls=w[now].rs=1;
 80         w[now].ans=1;
 81         return;
 82     }
 83     int m=L+R>>1;
 84     if(x<=m) modify(lc[now],L,m,x);
 85     else modify(rc[now],m+1,R,x);
 86     pushup(now);
 87     return;
 88 }
 89 node query(int now,int L,int R,int x,int y)
 90 {
 91     if(L>=x&&R<=y) return w[now];
 92     
 93     int m=L+R>>1;
 94     node n1,n2;
 95     
 96     if(x<=m) n1=query(lc[now],L,m,x,y);
 97     if(y>m) n2=query(rc[now],m+1,R,x,y);
 98     
 99     return update(n1,n2);
100 }
101 void Clear()
102 {
103     np=rt=0;
104     memset(w,0,sizeof(w));
105     memset(lc,0,sizeof(lc));
106     memset(rc,0,sizeof(rc));
107     memset(ANS,0,sizeof(ANS));//這玩意一定要註意清掉 
108 }
109 int main()
110 {
111     scanf("%d",&T);
112     int L1,R1,l1,r1;
113     while(T--)
114     {
115         Clear();
116         scanf("%d%d",&n,&m);
117         
118         build(rt,1,n);
119         
120         for(int i=1;i<=n;i++)
121         scanf("%d",&b[i].w),b[i].pos=i;
122         
123         for(int i=1;i<=m;i++)
124         {
125             scanf("%d%d%d%d",&L1,&R1,&l1,&r1); 
126             //把 m個詢問拆成 2m個 
127             a[i].L=a[m+i].L=L1;a[m+i].R=a[i].R=R1;
128             a[i].r=l1-1,a[m+i].r=r1;
129             
130             a[i].tot=-1,a[m+i].tot=1;//標記下操作的正負 
131             a[i].id=a[m+i].id=i;
132         }
133         
134         sort(a+1,a+2*m+1);
135         sort(b+1,b+n+1);
136         
137         int j=1;
138         
139         for(int i=1;i<=2*m;i++)
140         {
141             while(j<=n&&b[j].w<=a[i].r) 
142             modify(rt,1,n,b[j].pos),j++;
143                 
144             node Ans=query(rt,1,n,a[i].L,a[i].R);
145             
146             ANS[a[i].id]+=a[i].tot*Ans.ans;//這裏有ANS[] Ans ans要註意區分orz 
147         }
148         
149         for(int i=1;i<=m;i++) printf("%lld\n",ANS[i]);
150     }
151     return 0; 
152 }

最簡單的問題(重慶市第八屆大學生程序設計大賽D) (線段樹+離線思想)