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 }

猜你喜欢

转载自www.cnblogs.com/Qin-Wei-Kai/p/10054258.html
今日推荐