1. 程式人生 > >LNSYOJ203最大值【樹狀陣列應用】【做題報告+樹狀陣列深刻理解】

LNSYOJ203最大值【樹狀陣列應用】【做題報告+樹狀陣列深刻理解】

這道題是一個典型的樹狀陣列查詢有幾個比某個數大/小的數的應用

題目描述

   給定NN個區間,選定一個固定整數值TT,對於一個區間[ai,bi][ai,bi].

如果T<aiT<ai,那麼T在這個區間的得分為X,

如果T>biT>bi,那麼T在這個區間的得分為Z,

如果aiTbiai≤T≤bi的話T在這個區間的得分為Y。

選定一個合適的T,使得T在所有區間的得分的和最大。

 

輸入格式

第一行由空格隔開的4個整數N,X,Y,Z。 接下來N行每行由空格隔開的兩個數ai,bi。

輸出格式

一個整數表示最大的得分。

樣例一

input

4 7 9 6
5 8
3 4
13 20 
7 10

output

31

限制與約定

對於100%的資料,1N20000,0aibi109.0X,Y,Z10001≤N≤20000,0≤ai≤bi≤109.0≤X,Y,Z≤1000

時間限制1s1s

空間限制256MB

 

首先@Unstoppable728

感謝jdr大佬的講解!!!

這道題第一眼看以為是二分,後來發現根本不用,就是一個應用

樹狀陣列查詢有幾個比某個數大/小的數,

查詢有幾個比某個數小的數,很簡單,我之前就會

 1 void add(int pos,int val)
 2 {
 3     for(int i=pos;i<=tt;i+=lowbit(i))
 4         tree[i]+=val;
 5 }
 6 int ask(int pos,int op)
 7 {
 8     int ans=0;
 9     for(int i=pos;i;i-=lowbit(i))
10         ans+=tree[i];
11 return ans; 12 }

 只要在陣列下標位置加一或減一,然後累加字首和就OK;

然後最大值怎麼破,然後我的腦洞大開,把陣列全部顛倒過來存

 1 add(n-i+1,1); 2 ask(n-i+1-1,1); 

然後這道題是可以A的!

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define N 20011
 4 #define lowbit(a) (a)&(-a)
 5 using namespace std;
 6 int n,x,y,z,tt=0;
 7 int tree1[N*2],tree2[N*2];
 8 struct node{
 9     int val,op,hs;
10 }nd[N*2];
11 bool cmp(node a,node b){return a.val<b.val;}
12 void add(int pos,int val,int op)
13 {
14     switch(op)
15     {
16         case 1:
17             for(int i=pos;i<=tt;i+=lowbit(i))
18                 tree1[i]+=val;
19             break;
20         case 2:
21             for(int i=pos;i<=tt;i+=lowbit(i))
22                 tree2[i]+=val;
23             break;
24     }
25 }
26 int ask(int pos,int op)
27 {
28     int ans=0;
29     switch(op)
30     {
31         case 1:
32             for(int i=pos;i;i-=lowbit(i))
33                 ans+=tree1[i];
34             break;
35         case 2:
36             for(int i=pos;i;i-=lowbit(i))
37                 ans+=tree2[i];
38             break;
39     }
40     return ans;
41 }
42 int main()
43 {
44     scanf("%d%d%d%d",&n,&x,&y,&z);
45     for(int i=1;i<=n;i++)
46     {
47         scanf("%d%d",&nd[i].val,&nd[i+n].val);
48         nd[i].op=0,nd[i+n].op=1;
49     }
50     sort(nd+1,nd+2*n+1,cmp);
51     for(int i=1;i<=2*n;i++)
52     {
53         if(nd[i].val==nd[i-1].val)nd[i].hs=tt;
54         else nd[i].hs=++tt;
55     }
56     for(int i=1;i<=2*n;i++)
57     {
58         if(nd[i].op==0)add(tt-nd[i].hs+1,1,1);
59         else add(nd[i].hs,1,2);
60     }
61     int ans=-1;
62     for(int i=1;i<=tt;i++)
63     {
64         int aa=ask(tt-i+1-1,1),bb=ask(i-1,2);
65         int kk=z*bb+aa*x+(n-aa-bb)*y;
66         ans=max(ans,kk);
67     }
68     printf("%d\n",ans);
69     return 0;
70 }
我的玄學程式碼

然後jdr巨佬回來了,進行了一番詢問之後,

原來統計有幾個比它大的只要反著更新,正著查詢就OK

這是為什麼呢??

其實我們只要驗證樹狀陣列的可逆性就好

來看這張圖

進行腦補,會發現,其實樹狀陣列和二進位制有很大的關係,每一個1就是一個等級

樹狀陣列為什麼會成立?因為樹狀陣列的查詢和修改都是既不遺漏又不重複,而且會發現這個樹狀結構都是同構的,而且查詢跳躍方式都相同

看這張圖會發現比如說對於8(100),他包含著後兩位的所有組合表示的數,而其他的都不會包含8所包含的數,

也可以這樣去理解,查詢時在刪去每個1時,都會把每個1下能表示的所有數進行累加

動手來模擬下查詢和修改的過程,會發現它完全可以逆過來進行操作,它和陣列的性質完全一樣,只是將複雜度優化到了log級,這也就是為什麼它叫樹狀陣列

然後查有幾個比某一個數大的就很好理解了,因為一個數它會影響比他小的數的比它大的數量,所以向前更新,查詢是找它以後有幾個數,即向後查詢,也是完全OK的

感覺自己對樹狀陣列有了更深的理解!!

本題AC程式碼

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define N 20011
 4 #define lowbit(a) (a)&(-a)
 5 using namespace std;
 6 int n,x,y,z,tt=0;
 7 int tree1[N*2],tree2[N*2];
 8 struct node{
 9     int val,op,hs;
10 }nd[N*2];
11 bool cmp(node a,node b){return a.val<b.val;}
12 void add(int pos,int val,int op)
13 {
14     switch(op)
15     {
16         case 1:
17             for(int i=pos;i;i-=lowbit(i))
18                 tree1[i]+=val;
19             break;
20         case 2:
21             for(int i=pos;i<=tt;i+=lowbit(i))
22                 tree2[i]+=val;
23             break;
24     }
25 }
26 int ask(int pos,int op)
27 {
28     int ans=0;
29     switch(op)
30     {
31         case 1:
32             for(int i=pos;i<=tt;i+=lowbit(i))
33                 ans+=tree1[i];
34             break;
35         case 2:
36             for(int i=pos;i;i-=lowbit(i))
37                 ans+=tree2[i];
38             break;
39     }
40     return ans;
41 }
42 int main()
43 {
44     scanf("%d%d%d%d",&n,&x,&y,&z);
45     for(int i=1;i<=n;i++)
46     {
47         scanf("%d%d",&nd[i].val,&nd[i+n].val);
48         nd[i].op=0,nd[i+n].op=1;
49     }
50     sort(nd+1,nd+2*n+1,cmp);
51     for(int i=1;i<=2*n;i++)
52     {
53         if(nd[i].val==nd[i-1].val)nd[i].hs=tt;
54         else nd[i].hs=++tt;
55     }
56     for(int i=1;i<=2*n;i++)
57     {
58         if(nd[i].op==0)add(nd[i].hs,1,1);
59         else add(nd[i].hs,1,2);
60     }
61     int ans=-1;
62     for(int i=1;i<=tt;i++)
63     {
64         int aa=ask(i+1,1),bb=ask(i-1,2);
65         int kk=z*bb+aa*x+(n-aa-bb)*y;
66         ans=max(ans,kk);
67     }
68     printf("%d\n",ans);
69     return 0;
70 }