HDU 5919 Sequence II(主席树)题解

题意:有A1 ~ An组成的数组,给你l r,L = min((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1),R = max((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1),你先需要的到L,R区间有k个不同的数字,然后问你L,R区间第(k + 1)/ 2个不同的数字下标是多少?

思路:显然是个在线询问。

我们之前已经会用主席树求区间内有多少不同数字了:从左到右把每个数字的位置存进每个操作的线段树,如果之前这个数已经出现,就在当前这棵线段树中删掉之前出现的位置,以保证每个数字出现的唯一性。显然每个区间保存的是某个数字最右边出现的位置。

但是这里显然我们不能去直接求第(k + 1)/ 2个不同的数字下标,因为我这里要求的是最早出现第(k + 1)/ 2个数的位置。那我直接从n往1建主席树,那我就变成了每个区间保存的是某个数字最左边出现的位置,显然我第i棵树并没有保存i前面的位置,那我就可以直接求i到后面任意位置的区间的第p个不相同数出现的位置。

代码:

#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 10;
const int M = maxn * 30;
const ull seed = 131;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
int a[maxn], root[maxn], tot;
int n, q;
struct node{
    int lson, rson;
    int sum;
}T[maxn * 40];
int fa[maxn], ans[maxn];
void init(){
    tot = 0;
    memset(fa, -1, sizeof(fa));
    memset(T, 0, sizeof(T));
}
void update(int l, int r, int &now, int pre, int pos, int v){
    T[++tot] = T[pre], now = tot;
    T[now].sum += v;
    if(l == r) return;
    int m = (l + r) >> 1;
    if(pos <= m)
        update(l, m, T[now].lson, T[pre].lson, pos, v);
    else
        update(m + 1, r, T[now].rson, T[pre].rson, pos, v);
}
int query1(int l, int r, int L, int R, int now){
    if(L <= l && R >= r){
        return T[now].sum;
    }
    int m = (l + r) >> 1, sum = 0;
    if(L <= m)
        sum += query1(l, m, L, R, T[now].lson);
    if(R > m)
        sum += query1(m + 1, r, L, R, T[now].rson);
    return sum;
}
int query2(int l, int r, int now, int k){
    if(l == r) return l;
    int m = (l + r) >> 1;
    int sum = T[T[now].lson].sum;
    if(sum >= k)
        return query2(l, m, T[now].lson, k);
    else
        return query2(m + 1, r, T[now].rson, k - sum);
}
int main(){
    int ca = 1, t;
    scanf("%d" ,&t);
    while(t--){
        init();
        scanf("%d%d", &n, &q);
        root[n + 1] = 0;
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for(int i = n; i >= 1; i--){
            if(fa[a[i]] == -1){
                update(1, n, root[i], root[i + 1], i, 1);
                fa[a[i]] = i;
            }
            else{
                update(1, n, root[i], root[i + 1], i, 1);
                update(1, n, root[i], root[i], fa[a[i]], -1);
                fa[a[i]] = i;
            }
        }
        ans[0] = 0;
        for(int i = 1; i <= q; i++){
            int l, r, L, R, k;
            scanf("%d%d", &l, &r);
            L = min((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1);
            R = max((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1);
            k = query1(1, n, L, R, root[L]);
            ans[i] = query2(1, n, root[L], (k + 1) / 2);
        }
        printf("Case #%d:", ca++);
        for(int i = 1 ; i <= q; i++)
            printf(" %d", ans[i]);
        printf("\n");
    }

    return 0;
}

原文地址:https://www.cnblogs.com/KirinSB/p/10786248.html

时间: 2024-10-17 12:53:02

HDU 5919 Sequence II(主席树)题解的相关文章

HDU 5919 Sequence II 主席树

Sequence II Problem Description Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,?,an There are m queries. In the i-th query, you are given two integers li and ri. Consider the subsequence ali,ali+1,ali+2,?,ari. We can deno

HDU 5919 Sequence II(主席树+逆序思想)

Sequence II Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 1422    Accepted Submission(s): 362 Problem Description Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2

HDU 5919 - Sequence II (2016CCPC长春) 主席树 (区间第K小+区间不同值个数)

HDU 5919 题意: 动态处理一个序列的区间问题,对于一个给定序列,每次输入区间的左端点和右端点,输出这个区间中:每个数字第一次出现的位子留下, 输出这些位子中最中间的那个,就是(len+1)/2那个. 思路: 主席树操作,这里的思路是从n到1开始建树.其他就是主席树查询区间第K小,计算区间不同值个数. #include <algorithm> #include <iterator> #include <iostream> #include <cstring&

HDU 5919 -- Sequence II (主席树)

题意: 给一串数字,每个数字的位置是这个数第一次出现的位置. 每个询问对于序列的一个子区间,设一共有k个不同的数,求第ceil(k/2)个数的位置. 因为强制在线,所以离线乱搞pass掉. 主席树可解. 考虑一个数列: p      1 2 3 4 5 6  // 原序列标号 a : 1 2 1 2 3 4  // 原序列 p1 1 2 1 2 5 6  // 子序列开始下标为1 p2        2 3 1 5 6 p3           3 4 5 6 p4              4

HDU 5919 Sequence II

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5919 ------------------------------------------------------------------------------------ 现场赛的时候由于不会主席树就用分块卡过去了 现在学习了主席树后感觉这题也是很有意思的 首先 这题要把问题转化为对于一段区间 求一个左端点相同的$($如果有多个取右端点最靠左的$)$子区间 这个子区间的不同数的个数等于原区间不同

[ACM] hdu 5147 Sequence II (树状数组,前缀和,后缀和)

Sequence II Problem Description Long long ago, there is a sequence A with length n. All numbers in this sequence is no smaller than 1 and no bigger than n, and all numbers are different in this sequence. Please calculate how many quad (a,b,c,d) satis

Hdu 5147 Sequence II(树状数字 or 线段树 + 输入外挂 前缀和+后缀和)

题意: 给定1~n的一个排列 用A[]数组保存,问有多少下标(a,b,c,d)四元组满足: a 解析: 题目中n的范围是50000,O(n^2) 复杂度肯定超时.那么这题明显考察的是log2(n)的算法,对于这题可以用线段树或者树状数组,同时要用到输入外挂,不然会超时. 思路(参考别人做法) 枚举c的位置,那么每一次枚举中的方法数为 1~c-1 中(a,b)的个数 乘以 c~n中(c,d)的个数.累加起来即为答案. 1~c-1中(a,b)的个数相当于枚举b的位置,然后计算出b前面有多少数比A[b

hdu 5147 Sequence II(树状数组)

题目链接:hdu 5147 Sequence II 预处理每个位置作为b和c可以组成的对数,然后枚举b的位置计算. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 50005; int N, arr[maxn], fenw[maxn], lef[maxn], rig[maxn]; #d

HDU 3397 Sequence operation 线段树

线段树大杂烩~ 各种操作都有,细心点不难1A #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; #define lson rt<<1,l,mid #define rson rt<<1|1,mid + 1,r const int maxn = 1e5 + 10; int lmax[maxn