HDU 5919 Sequence II(主席树)

本文介绍了一种使用主席树解决强制在线查询问题的方法。该问题要求对于给定序列,在线处理查询请求,找到指定区间内不同数值首次出现位置的中位数。文章通过构建线段树,并利用主席树进行高效更新和查询操作,实现了强制在线处理要求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

有长度为n的序列,强制在线询问[l,r]这段区间中所有不同数出现的第一个位置,按照位置从小到大排完序以后的中间(向上取整)的那个位置是多少?

分析:

  1. 如果可以离线做,那么按照l排个序以后,从右想左逐个添加新的位置,删除前一个位置。
  2. 但是这个强制在线怎么搞, 那就把以[l,n]序列形成的线段树都存下来,然后在对应的线段树上面搞一下,就行了。这个时候用主席树来存就好了。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <string>

using namespace std;

typedef long long LL;
typedef vector <int>    VI;
typedef pair <int,int>  PII;
#define FOR(i,x,y)  for(int i = (int)(x);i < (int)(y);++ i)
#define IFOR(i,x,y) for(int i = (int)(x);i > (int)(y);-- i)
#define pb  push_back
#define mp  make_pair
#define fi  first
#define se  second

const int maxn = 200005;

struct Tree{
    int ls, rs, sum;
}tree[maxn*50];

int rt[maxn], tot, sz;

int build(int l,int r) {
    int o = tot ++;
    tree[o].sum = 0;
    if(l == r) return o;
    int mid = (l+r)>>1;
    tree[o].ls = build(l, mid);
    tree[o].rs = build(mid+1, r);
    return o;
}

int update(int x, int l, int r, int lt, int v) {
    int o = tot ++;
    tree[o] = tree[lt];
    tree[o].sum += v;
    if(l == r) return o;
    int mid = (l+r)>>1;
    if(x <= mid) tree[o].ls = update(x, l, mid, tree[lt].ls, v);
    else tree[o].rs = update(x, mid+1, r, tree[lt].rs, v);
    return o;
}

int query(int i, int l, int r, int ll, int rr) {
    if(ll == l && rr == r)     return tree[i].sum;
    int mid = (l+r)>>1;
    if(rr <= mid)     return query(tree[i].ls, l, mid, ll, rr);
    else if(ll > mid) return query(tree[i].rs, mid+1, r, ll, rr);
    else return query(tree[i].ls, l, mid, ll, mid)+query(tree[i].rs, mid+1, r, mid+1, rr);
}

int query2(int i, int l, int r, int v) {
    if(l == r) return l;
    int mid = (l+r)>>1;
    if(v <= tree[tree[i].ls].sum) return query2(tree[i].ls, l, mid, v);
    else return query2(tree[i].rs, mid+1, r, v-tree[tree[i].ls].sum);
}

int n, m, a[maxn], id[maxn], nt[maxn], ans[maxn];

int main() {
    //freopen("test.in", "r", stdin);
    int T, tCase = 0;     scanf("%d", &T);
    while(T--) {
        printf("Case #%d:", ++ tCase);
        scanf("%d%d", &n, &m);
        FOR(i, 1, n+1) scanf("%d", a+i);
        sz = n+1;
        FOR(i, 1, n+2)     id[i] = n+1;
        IFOR(i, n, 0)     nt[i] = id[a[i]], id[a[i]] = i;
        tot = 0;
        rt[n+1] = build(1, sz);
        IFOR(i, n, 0) {
            if(nt[i] == n+1) {rt[i] = update(i, 1, sz, rt[i+1], 1); continue;}
            int o = update(nt[i], 1, sz, rt[i+1], -1);
            rt[i] = update(i, 1, sz, o, 1); 
        }
        ans[0] = 0;
        FOR(i, 1, m+1) {
            int l, r;
            scanf("%d%d", &l, &r);
            l = (l+ans[i-1]%n)%n+1; r = (r+ans[i-1]%n)%n+1;
            if(l > r) swap(l, r);
            int sum = query(rt[l], 1, sz, 1, r);
            //cout << i << "\n" << "sum:" << sum << " l:" << l << " r:" << r << endl;
            sum = (sum+1)>>1;
            ans[i] = query2(rt[l], 1, sz, sum);
            printf(" %d", ans[i]);
        }
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值