最简单的问题(重庆市第八届大学生程序设计大赛D) (线段树+离线思想)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最简单的问题(重庆市第八届大学生程序设计大赛D) (线段树+离线思想)相关的知识,希望对你有一定的参考价值。

技术分享图片

 

考场上的时候直接一脸懵逼了,啥? 区间里面又要求子区间,还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) (线段树+离线思想)的主要内容,如果未能解决你的问题,请参考以下文章

南京理工大学第八届程序设计大赛(校外镜像)题解报告

2017年中国大学生程序设计竞赛-中南地区赛暨第八届湘潭市大学生计算机程序设计大赛游记心得

第八届中国国际“互联网+”大学生创新创业大赛介绍

第八届“互联网+”大赛|百度杰出架构师毕然解读产业赛道命题

翻译贴上海市第八届星光计划(2019年)技能大赛网站设计(中职组)决赛题目

nyoj 1239 引水工程 (河南省第八届acm程序设计大赛)