bzoj2653middle 主席树+二分答案

2653: middle

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 1855   Solved: 1031
[ 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


Source


clj的题目果然难啊。。。膜拜各种题解之后写出来的
如果只是求某区间中位数的话考虑可以使用主席树维护权值然后bst一下
然而多了个最大,所以就要二分答案(sb没有想到,基础不够扎实)
二分答案x后如果是定区间只需要验证方案的合法,而动区间则需要构造函数来判断这个值是否可以增大。
显然中位数是相对大小的产物,因此用1代表大于等于x,-1代表小于x,那么对与某个区间的一段转换为1和-1的子区间,如果它的和大于0,显然中位数要更大。很经典的转化方法。
问题到目前为止转化为:给定一个序列,序列中的数均是1或-1,求起始下标为[a,b],终止下标为[c,d]所有子序列中最大的子序列和是否大于0。这显然是一个最大字段和问题。
发现(b,c)是必取的,而最大化的目标可以转化为已b为终止下标在区间[a,b]上的最大字段和和以c为起始下标在区间[c,d]上的最大子段和。这显然对应的是线段树中维护左子区间最大和右子区间最大的做法。
最后一个问题就是,对于不同的x如何维护它对应的线段树。我们发现,如果x转移到x的后继,那么只有一个值x会发生变化。这样就可以直接套用主席树模板。具体来说:先把初始每个数设置为1,然后从小到大吧每个数插入一颗以位置主席树中并将其改成-1
整理一下做法和思路:对于最大的中位数,先构造根据每个数在原区间上的相对大小的序列,及大于等于这个数就为1,否则为-1,这个用主席树维护,然后二分答案x,判断以x为中位数可大还是可小,在主席树上找(b,c)和rm[a,b]和lm[c,d]相加,若大于零则x可能增大,小于零则x必须减小。

一道高级数据结构里蕴含了动态规划与构造性的思想,实在是一道难得的好题。

/**************************************************************
    Problem: 2653
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:2264 ms
    Memory:14168 kb
****************************************************************/
 
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define maxn 32000
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
struct data {
    int val, id;
    bool operator < (const data &a) const {
        return val == a.val ? id < a.id : val < a.val;
    }
}a[maxn];
int n, m, sz, root[maxn];
int sum[maxn * 20], rs[maxn * 20], ls[maxn * 20], lm[maxn * 20], rm[maxn * 20];
 
void update(int p) {
    sum[p] = sum[ls[p]] + sum[rs[p]];
    lm[p] = max(lm[ls[p]], sum[ls[p]] + lm[rs[p]]);
    rm[p] = max(rm[rs[p]], sum[rs[p]] + rm[ls[p]]);
}
 
void build(int &p, int L, int R) {
    p = ++sz;
    if(L == R) {
        sum[p] = lm[p] = rm[p] = 1;
        return;
    }
    int mid = L + R >> 1;
    build(ls[p], L, mid);
    build(rs[p], mid + 1, R);
    update(p);
}
 
void add_tree(int &cur_p, int pre_p, int pos, int L, int R, int val) {
    cur_p = ++sz;
    if(L == R) {
        sum[cur_p] = lm[cur_p] = rm[cur_p] = val;
        return;
    }
    int mid = L + R >> 1;
    if(pos <= mid) {
        rs[cur_p] = rs[pre_p];
        add_tree(ls[cur_p], ls[pre_p], pos, L, mid, val);
    }
    else {
        ls[cur_p] = ls[pre_p];
        add_tree(rs[cur_p], rs[pre_p], pos, mid + 1, R, val);
    }
    update(cur_p);
}
 
void init() {
    n = read();
    for(int i = 0;i < n; ++i) {
        scanf("%d", &a[i].val);
        a[i].id = i;
    }
    sort(a, a + n); lm[0] = rm[0] = -inf;
    build(root[0], 0, n - 1);
    for(int i = 1;i < n; ++i) add_tree(root[i], root[i - 1], a[i - 1].id, 0, n - 1, -1);
}
 
int query_sum(int p, int st, int ed, int L, int R) {
    if(st > ed) return 0;
    if(st == L && ed == R) return sum[p];
    int ans = 0, mid = L + R >> 1;
    if(st <= mid) ans += query_sum(ls[p], st, min(mid, ed), L, mid);
    if(ed > mid) ans += query_sum(rs[p], max(mid + 1, st), ed, mid + 1, R);
    return ans;
}
 
int query_lm(int p, int st, int ed, int L, int R) {
    if(st == L && ed == R) return lm[p];
    int ans = -inf, mid = L + R >> 1;
    if(st <= mid) ans = max(ans, query_lm(ls[p], st, min(ed, mid), L, mid));
    if(ed > mid) ans = max(ans, query_sum(ls[p], st, mid, L, mid) + query_lm(rs[p], max(mid + 1, st), ed, mid + 1, R));
    return ans;
}
 
int query_rm(int p, int st, int ed, int L, int R) {
    if(st == L && ed == R) return rm[p];
    int ans = -inf, mid = L + R >> 1;
    if(ed > mid) ans = max(ans, query_rm(rs[p], max(mid + 1, st), ed, mid + 1, R));
    if(st <= mid) ans = max(ans, query_sum(rs[p], mid + 1, ed, mid + 1, R) + query_rm(ls[p], st, min(ed, mid), L, mid));
    return ans;
}
 
bool judge(int cur, int a, int b, int c, int d) {
    int ret = query_sum(root[cur], b + 1, c - 1, 0, n - 1);
    ret += query_rm(root[cur], a, b, 0, n - 1);
    ret += query_lm(root[cur], c, d, 0, n - 1);
    return ret >= 0;
}
 
void solve() {
    m = read(); int ans = 0, q[4];
    while(m--) {
        for(int i = 0;i < 4; ++i) q[i] = (read() + ans) % n;
        sort(q, q + 4);
        int L = 0, R = n - 1;
        while(L < R) {
            int mid = L + R + 1 >> 1;
            if(judge(mid, q[0], q[1], q[2], q[3])) L = mid;
            else R = mid - 1;
        }
        printf("%d\n", ans = a[L].val);
    }
}
 
int main()
{
    init();
    solve(); 
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
校园悬赏任务平台对字典管理、论坛管理、任务资讯任务资讯公告管理、接取用户管理、任务管理、任务咨询管理、任务收藏管理、任务评价管理、任务订单管理、发布用户管理、管理员管理等进行集中化处理。经过前面自己查阅的网络知识,加上自己在学校课堂上学习的知识,决定开发系统选择小程序模式这种高效率的模式完成系统功能开发。这种模式让操作员基于浏览器的方式进行网站访问,采用的主流的Java语言这种面向对象的语言进行校园悬赏任务平台程序的开发,在数据库的选择上面,选择功能强大的Mysql数据库进行数据的存放操作。校园悬赏任务平台的开发让用户查看任务信息变得容易,让管理员高效管理任务信息。 校园悬赏任务平台具有管理员角色,用户角色,这几个操作权限。 校园悬赏任务平台针对管理员设置的功能有:添加并管理各种类型信息,管理用户账户信息,管理任务信息,管理任务资讯公告信息等内容。 校园悬赏任务平台针对用户设置的功能有:查看并修改个人信息,查看任务信息,查看任务资讯公告信息等内容。 系统登录功能是程序必不可少的功能,在登录页面必填的数据有两项,一项就是账号,另一项数据就是密码,当管理员正确填写并提交这二者数据之后,管理员就可以进入系统后台功能操作区。项目管理页面提供的功能操作有:查看任务,删除任务操作,新增任务操作,修改任务操作。任务资讯公告信息管理页面提供的功能操作有:新增任务资讯公告,修改任务资讯公告,删除任务资讯公告操作。任务资讯公告类型管理页面显示所有任务资讯公告类型,在此页面既可以让管理员添加新的任务资讯公告信息类型,也能对已有的任务资讯公告类型信息执行编辑新,失效的任务资讯公告类型信息也能让管理员快速删除。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值