数据结构_主席树_HDU 5919 Sequence II

HDU 5919 Sequence II

CCPC长春赛区现场赛的题,可惜自己太菜,当时不会做,听了老哥的教训后,决定好好学习主席树。


思路:考虑每个点带来的影响。

显然,若从前向后考虑,对于第i个数,对结果的影响仅为
1. 若该数字未出现过,添加一个新位置i
2. 若出现过,无操作。
但是“这种影响是不全面的”,因为它无法表现从i起始时的结果。

如果从后向前考虑,每个点对结果的影响为
1. 更新某个数字出现的位置。
2.1 若该数字未出现过,则添加新的位置
2.2 若出现过,则消去前面曾经出现的位置,并添加新的位置
我们可以表现从任意起始时的结果。

#include <iostream>
#include <cstdio>
#include<cstring>
#include <algorithm>
using namespace std;
#define lson tn[x].ls
#define rson tn[x].rs

const int maxn = 200000;

struct TreeNode {
    int ls, rs; //左右子节点
    int value;    //值
};

TreeNode tn[maxn * 50];
int POS, VALUE;
int cnt;
int N, M;
int a[maxn + 10], mp[maxn + 10];
int tr[maxn + 10]; //tr[i] 树根i的节点编号

int node(int v, int ls, int rs) {
    tn[cnt].value = v;
    tn[cnt].ls = ls;
    tn[cnt].rs = rs;
    return cnt ++;
}

void Maintain(int x) {
    tn[x].value = tn[lson].value + tn[rson].value;
}

/*
 * 建空树
 */
void Build(int &x, int l = 1, int r = N) {
    x = node(0, -1, -1);
    if(l == r) {
        return;
    }

    int mid = (l + r) >> 1;
    Build(lson, l, mid);
    Build(rson, mid + 1, r);
}

/*
 * 插入新节点
 */
void Insert(int pre, int &x, int l = 1, int r = N) {
    x = node(tn[pre].value, tn[pre].ls, tn[pre].rs);
    if(l == r) {
        tn[x].value = VALUE;
        return;
    }

    int m = (l + r) >> 1;
    if(POS <= m) {
        Insert(lson, lson, l, m);
    } else {
        Insert(rson, rson, m + 1, r);
    }
    Maintain(x);
}

/*
 * 查找根为x时的,前k个数的和
 */
int Query(int x, int ll, int rr, int l = 1, int r = N) {
    if(ll <= l && r <= rr) {
        return tn[x].value;
    }
    int m = (l + r) >> 1;
    int ans = 0;
    if(ll <= m)
        ans += Query(lson, ll, rr, l, m);
    if(rr > m) {
        ans += Query(rson, ll, rr, m + 1, r);
    }
    return ans;
}

/*
 * 查找根为x时的,第k个数
 */
int Find(int x, int k, int l = 1, int r = N) {
    if(l == r) {
        return l;
    }
    int tmp = tn[lson].value;
    int m = (l + r) >> 1;
    if(k <= tmp)return Find(lson, k, l, m);
    else return Find(rson, k - tmp, m + 1, r);
}

int main() {
    int T, k;
    scanf("%d", &T);
    for(int CASE = 1; CASE <= T; CASE++) {
        memset(mp, -1, sizeof(mp));
        scanf("%d%d", &N, &M);
        for(int i = 1; i <= N; i ++) {
            scanf("%d", &a[i]);
        }
        cnt = 1;
        Build(tr[N + 1]);

        for(int i = N; i > 0; i --) {
            if(mp[a[i]] == -1) {
                POS = i;
                VALUE = 1;
                Insert(tr[i + 1], tr[i]);
            } else {
                POS = i;
                VALUE = 1;
                Insert(tr[i + 1], tr[i]);
                POS = mp[a[i]];
                VALUE = 0;
                Insert(tr[i], tr[i]);
            }
            mp[a[i]] = i;
        }

        int ans = 0;
        printf("Case #%d:", CASE);
        while(M --) {
            int l, r;
            scanf("%d%d", &l, &r);

            l = (l + ans) % N + 1;
            r = (r + ans) % N + 1;
            if(r < l) swap(l, r);
            int all = Query(tr[l], l, r);
            printf(" %d", ans = Find(tr[l], (all + 1) >> 1));
        }
        puts("");
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值