51nod 1463 找朋友(线段树+离线处理)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1463

题意:

思路:

好题!

先对所有查询进行离线处理,按照右区间排序,因为k一共最多只有10个,所有在该区间内的B数组,每次枚举K值,通过这样的方式来得到另外一个B值。但是这样得到的B值它在B数组中的位置必须在当前数的左边。如下图:(j为当前数在B数组中的位置,pos为计算得到的另一个B值在数组中的位置)

这两个数的和记录在pos中,这里pos的位置必须在j的左边,假设第q个查询区间如上图所示(请在脑中将pos和j互换一下),那么此时j就没包含在该区间内,这样一来,查询得到的值就会有错。因为我们是按照右区间排序的,所以右区间会不断扩大,只要左边被覆盖的,那么右边的数肯定是在该区间内的。

用线段树维护即可。具体请参见代码。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<vector>
  6 #include<stack>
  7 #include<queue>
  8 #include<cmath>
  9 #include<map>
 10 #include<set>
 11 using namespace std;
 12 typedef long long ll;
 13 const int INF = 0x3f3f3f3f;
 14 const int maxn=1e5+5;
 15
 16 int n,q,m;
 17 int A[maxn],B[maxn],K[15],B_pos[maxn],ans[maxn];
 18
 19 struct node
 20 {
 21     int l,r,id;
 22     bool operator<(const node& rhs) const
 23     {
 24         return r<rhs.r;
 25     }
 26 }Q[maxn];
 27
 28 int MAX[maxn<<2];
 29 int val[maxn<<2];
 30
 31 void build(int l, int r, int o)
 32 {
 33     val[o]=0;
 34     if(l==r)  return;
 35     int mid=(l+r)>>1;
 36     build(l,mid,o<<1);
 37     build(mid+1,r,o<<1|1);
 38 }
 39
 40 void update(int l, int r, int pos, int x, int o)
 41 {
 42     val[o]=max(val[o],x);
 43     if(l==pos && r==pos)  return;
 44     int mid=(l+r)>>1;
 45     if(pos<=mid)  update(l,mid,pos,x,o<<1);
 46     else   update(mid+1,r,pos,x,o<<1|1);
 47 }
 48
 49 int query(int ql, int qr, int l, int r, int o)
 50 {
 51     if(ql<=l && qr>=r)  return val[o];
 52     int mid=(l+r)>>1;
 53     int res=0;
 54     if(ql<=mid)  res=max(res,query(ql,qr,l,mid,o<<1));
 55     if(qr>mid)   res=max(res,query(ql,qr,mid+1,r,o<<1|1));
 56     return res;
 57 }
 58
 59 int main()
 60 {
 61    //freopen("in.txt","r",stdin);
 62     scanf("%d%d%d",&n,&q,&m);
 63     for(int i=1;i<=n;i++)  scanf("%d",&A[i]);
 64     for(int i=1;i<=n;i++)  {scanf("%d",&B[i]);B_pos[B[i]]=i;}
 65     for(int i=1;i<=m;i++)  scanf("%d",&K[i]);
 66     for(int i=1;i<=q;i++)
 67     {
 68         scanf("%d%d",&Q[i].l,&Q[i].r);
 69         Q[i].id=i;
 70     }
 71     sort(Q+1,Q+q+1);
 72     build(1,n,1);
 73     int s=1;
 74     memset(MAX,0,sizeof(MAX));
 75     for(int i=1;i<=q;i++)
 76     {
 77         int r=Q[i].r;
 78         for(int j=s;j<=r;j++)
 79         {
 80             for(int k=1;k<=m;k++)
 81             {
 82                 int tmp_B=B[j]+K[k];
 83                 int pos=B_pos[tmp_B];  //得到tmp_B在B数组中的位置
 84                 if(tmp_B<=n && pos<j && A[pos]+A[j]>MAX[pos])
 85                 {
 86                     MAX[pos]=A[pos]+A[j];  //此时pos位置的最大值是由pos和[1,j)之间的一个数相加而成
 87                     update(1,n,pos,MAX[pos],1); //更新线段树
 88                 }
 89                 tmp_B=B[j]-K[k];
 90                 pos=B_pos[tmp_B];
 91                 if(tmp_B>=1 && pos<j && A[pos]+A[j]>MAX[pos])
 92                 {
 93                     MAX[pos]=A[pos]+A[j];
 94                     update(1,n,pos,MAX[pos],1);
 95                 }
 96             }
 97         }
 98         ans[Q[i].id]=query(Q[i].l,Q[i].r,1,n,1); //查询该区间内的最大值
 99         s=r;
100     }
101     for(int i=1;i<=q;i++)  printf("%d\n",ans[i]);
102     return 0;
103 }
时间: 2024-08-24 15:55:45

51nod 1463 找朋友(线段树+离线处理)的相关文章

51nod 1463 找朋友 (扫描线+线段树)

1463 找朋友  基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80 难度:5级算法题  收藏  关注 给定: 两个长度为n的数列A .B 一个有m个元素的集合K 询问Q次 每次询问[l,r],输出区间内满足|Bi-Bj|∈K 的最大Ai+Aj 数据约定: n,Q<=100000 m <= 10 0<=A[i]<=1000000000 1<=B[i]<=n 1<=K[i]<=n 保证B[i]互不相等 Input n Q m A1 A2 .

线段树+离线 hdu5654 xiaoxin and his watermelon candy

传送门:点击打开链接 题意:一个三元组假设满足j=i+1,k=j+1,ai<=aj<=ak,那么就好的.如今告诉你序列.然后Q次询问.每次询问一个区间[l,r],问区间里有多少个三元组满足要求 思路:刚開始看错题目了,原来三元组是连续3个,这作为bc最后一题也太水了把. . . 先一遍预处理.把连续3个满足条件的找出来,放到还有一个数组里排序去重,用这个数组来给三元组哈希.再扫一遍给三元组在之前那个排序好的数组里二分一下得到下标,大概就是哈希一下,用一个数字来表示. 之后的查询.事实上就是.在

Super Mario(线段树离线区间k值)

以前见过这题,没做出来,知道是离线处理,这次仔细想了下, 首先把出现的高度都map离散化一下,以离散化出来的数目g建树,把每个位置都开俩个vector,一个存以这个位置为L的询问,一个存以这个位置为R的询问. 然后从1-g 进行更新,假如当前i是以第j个区间的开始位置,那么这时就可以询问一下<=p[j].h的个数s,显然这时第J个区间多加的,需要减掉,p[j].sum-=s; 然后更新第i个数,update(a[i],1,g,1);再找到某第k区间是以i结尾的,那么依旧询问一下,得出s,p[k]

gcd(线段树离线处理)——HDU 4630

对应HDU题目:点击打开链接 No Pain No Game Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1801    Accepted Submission(s): 770 Problem Description Life is a game,and you lose it,so you suicide. But you can

SPOJ--K-query (线段树离线) 离线操作解决一下问题

K-query Given a sequence of n numbers a1, a2, ..., an and a number of k- queries. A k-query is a triple (i, j, k) (1 ≤ i ≤ j ≤ n). For each k-query (i, j, k), you have to return the number of elements greater than k in the subsequence ai, ai+1, ...,

玲珑oj 1117 线段树+离线+离散化,laz大法

1117 - RE:从零开始的异世界生活 Time Limit:1s Memory Limit:256MByte Submissions:438Solved:68 DESCRIPTION 486到了异世界,看到了一群可爱的妹子比如蕾姆啊,艾米莉亚啊,拉姆啊,白鲸啊,怠惰啊等等!有一天膜女告诉486说她的能力可能不能再用了,因为膜女在思考一个数据结构题,没心情管486了.486说我来帮你做,膜女说你很棒棒哦! 给一个集合,最开始为空(不是数学上的集合)五个操作: 1.插入x2.把小于x的数变成x3

HDU3874 线段树 + 离线处理

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3874 , 线段树(或树状数组) + 离线处理 下午做了第一道离线处理的题目(HDU4417),多少有点感觉,顺便就把这道题也给做了. 这道题就是要求某个区间内不重复数的和,自己在网上百度后参考别人的方法才AC的.参考来源:http://www.cnblogs.com/gj-Acit/p/3249827.html 这道题还是需要先离线处理数据,具体方法: ① 先用线段树把树给建立起来: ② 先把所有要

HDU4417 线段树 + 离线处理

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 , 线段树(或树状数组) + 离线处理 最近看了几道线段树的题都是需要离线处理数据的,正好这块比较手生,就练练了.  这道题主要的地方就是离线处理数据,具体想法: ① 先把所有位置的高度都存下来,然后排序,注意保存下标: ② 把所有询问存下来,然后按照询问的高度进行排序,同注意保存下标: ③ 对于排序后的每次询问的处理:由于每个位置的高度都已经存了下来并且进行了排序,所以可以按照顺序将每个点插

HDU 4638-Group(线段树+离线处理)

题意: 给n个编号,m个查询每个查询l,r,求下标区间[l,r]中能分成标号连续的组数(一组内的标号是连续的) 分析: 我们认为初始,每个标号为一个组(线段树维护区间组数),从左向右扫序列,当前标号,要考虑和他相邻的标号的位置,若前面位置出现了和它相邻的标号, 则前面位置组数减一(因为可以合并成一组),查询区间离线处理,保证了查询的正确. #include <map> #include <set> #include <list> #include <cmath&g