bzoj2653: middle

2653: middle

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 1770   Solved: 990
[ Submit][ Status][ Discuss]

Description

一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。

Input

第一行序列长度n。接下来n行按顺序给出a中的数。
接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是
x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的
要询问的a=q[0],b=q[1],c=q[2],d=q[3]。  
输入保证满足条件。
第一行所谓“排过序”指的是从大到小排序!

Output

Q行依次给出询问的答案。

Sample Input

5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0

271451044
271451044
969056313

Sample Output

HINT

  0:n,Q<=100

1,...,5:n<=2000

0,...,19:n<=20000,Q<=25000


今天颓了一下午这道题,找错找到我吃了shi一样。
结果打错了一个字母。(滑稽)
然后去跟tyb大神play ping-pong了
现在才写blog
题解:
一开始没想到二分,然后去看了路标。
知道是二分+主席树
二分答案,如果取件内大于ans的+1,否则的话-1
如果总和大于等于0的话证明ans还可以大。
至于check,我们可以对于每个-1建一棵树
对于[a,b]这一段我们找最大的后缀和,[c,d]这段找最小的前缀和。
b,c这一段的和肯定是不变的所以直接加上就好了
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 200020
#define M 8005000
struct xy{int o,num;}a[N];
int cmp(xy a,xy b){return a.num < b.num;}
int root[N],n,q;
int ls[M],rs[M],lm[M],rm[M],sum[M];
int cnt;
void push_up(int k){
   sum[k] = sum[ls[k]] + sum[rs[k]];
   lm[k] = max(lm[ls[k]],sum[ls[k]] + lm[rs[k]]);
   rm[k] = max(rm[rs[k]],sum[rs[k]] + rm[ls[k]]);
}
void build(int &rt,int l,int r){
   rt = ++ cnt;
   if (l == r) {
       lm[rt] = rm[rt] = sum[rt] = 1;
       return;
   }
   int m = l + r >> 1;
   build(ls[rt],l,m);
   build(rs[rt],m + 1,r);
   push_up(rt);
}
void update(int last,int l,int r,int &rt,int k,int val){
   rt = ++ cnt;
   ls[rt] = ls[last],rs[rt] = rs[last];
   if (l == r){
       sum[rt] = lm[rt] = rm[rt] = val;
       return;
   }
   int m = l + r >> 1;
   if (k <= m) update(ls[last],l,m,ls[rt],k,val);
   else update(rs[last],m + 1,r,rs[rt],k,val);
   push_up(rt);
}
int qall(int rt,int l,int r,int x,int y){
   if (l == x && r == y) return sum[rt];
   int m = l + r >> 1;
   if (y <= m) return qall(ls[rt],l,m,x,y);
   else if (x > m) return qall(rs[rt],m + 1,r,x,y);
   else return qall(ls[rt],l,m,x,m) + qall(rs[rt],m + 1,r,m + 1,y);
}
int ql(int rt,int l,int r,int x,int y){
   if (l == x && r == y) return lm[rt];
   int m = l + r >> 1;
   if (y <= m) return ql(ls[rt],l,m,x,y);
   else if (x > m) return ql(rs[rt],m + 1,r,x,y);
   else return max(ql(ls[rt],l,m,x,m),qall(ls[rt],l,m,x,m) + ql(rs[rt],m + 1,r,m + 1,y));
}
int qr(int rt,int l,int r,int x,int y){
   printf("%d %d %d %d %d %d\n",rt,l,r,x,y,rm[rt]);
    system("pause");
   if (l == x && r == y) return rm[rt];
   int m = l + r >> 1;
   if (y <= m) return qr(ls[rt],l,m,x,y);
   else if (x > m) return qr(rs[rt],m + 1,r,x,y);
   else return max(qr(rs[rt],m + 1,r,m + 1,y),qall(rs[rt],m + 1,r,m + 1,y) + qr(ls[rt],l,m,x,m));
}
int check(int k,int a,int b,int c,int d){
	//printf("%d %d %d %d\n",a,b,c,d);
   int ret = 0;
   if (c > b + 1) ret = qall(root[k],0,n - 1,b + 1,c - 1);
  //printf("%d ",ret);
   ret += ql(root[k],0,n - 1,c,d);
  // printf("%d ",ret);
   ret += qr(root[k],0,n - 1,a,b);
  // printf("%d\n",ret);
   return ret >= 0;
}
int main(){
   scanf("%d",&n);
   for (int i = 0;i < n;i ++){
       a[i].o = i;
       scanf("%d",&a[i].num);
   }
   sort(a,a + n,cmp);
   build(root[0],0,n - 1);
   for (int i = 1;i < n;i ++) update(root[i - 1],0,n - 1,root[i],a[i - 1].o,-1);
   	//for(int i=1;i<=n;i++) printf("%d\n",root[i]);
   scanf("%d",&q);
   int ans = 0,p[4];
   while (q --){
       scanf("%d%d%d%d",&p[0],&p[1],&p[2],&p[3]);
       for (int i = 0;i < 4;i ++) p[i] = (ans + p[i]) % n;
       sort(p,p + 4);
       int l = 0,r = n,mid;
       while (l < r - 1){
           mid = l + r >> 1;
           if (check(mid,p[0],p[1],p[2],p[3])) l = mid;
           else r = mid;
       }
       ans = a[l].num;
       printf("%d\n",ans);
   }
   return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值