JZOJ5710. 【北大夏令营2018模拟5.13】Mex

Description

在组合游戏中计算状态的 SG 值时,我们常常会遇到 mex 函数。mex(S) 的值为集合 S 中没有出现过的最小自然数。例如,mex({1,2}) = 0、mex({0,1,2,3}) = 4。
给定长度为 n 的序列 a。现有 m 次询问,每次给定 l 和 r,询问区间 [l,r] 的数构成的集合的 mex 值。

Input

输入数据的第一行包含三个整数 n、m 和 t,其中 t 为 0 或者 1,表示数据类型。
接下来一行,包含 n 个非负整数,为序列 a。
接下来 m 行,每行描述一个询问。第 i 行包含两个正整数 l 和 r,代表第 i 次询问的区间的左右端点。如果 t = 1,则询问进行了加密,从第二个询问开始,读入的 l 和 r 异或前一次询问的答案才是真正的询问左右端点。

Output

对于每个询问,输出一行,代表询问区间的 mex 值。

Sample Input

5 4 0
2 1 0 2 1
3 3
2 3
2 4
1 2

Sample Output

1
2
3
0
这里写图片描述

题解

题目就是求某一段数的mex,强制在线。
最简单的做法就是记录某个数在前面有没有出现过,
而现在需要求一段区间的时候,
就应该记录每个数最后一次出现的位置。
而对于每一个位置都维护这么多信息时不可取的,
每次只增加一个位置,
可以想到用主席树维护。

每个区间就维护,这个区间里面所有数字最后一次出现的位置的最小值,
查询的时候,如果这个区间最后一次出现的位置的最小值小于l,
那么就说明mex就在这个区间里面。

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 200003
#define M 1000000003
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

struct node
{
    int l,r,s;
}tr[N*40];

int n,m,T,tot,t,ans,opl,opr,pos;

void ins(int x,int y,int l,int r)
{
    tr[x]=tr[y];
    if(l==r)
    {
        tr[x].s=pos;
        return;
    }
    int m=(l+r)>>1;
    if(t<=m)ins(tr[x].l=++tot,tr[y].l,l,m);
        else ins(tr[x].r=++tot,tr[y].r,m+1,r);
    tr[x].s=min(tr[tr[x].l].s,tr[tr[x].r].s);
}

int find(int x,int l,int r)
{
    if(l==r)return l;
    int m=(l+r)>>1;
    if(tr[tr[x].l].s<opl)return find(tr[x].l,l,m);
        else return find(tr[x].r,m+1,r);
}

int main()
{
    freopen("mex.in","r",stdin);
    freopen("mex.out","w",stdout);

    read(n);read(m);read(T);tot=n;
    for(register int i=1;i<=n;i++)
    {
        read(t);pos=i;t++;
        ins(i,i-1,1,M);
    }

    ans=0;
    for(register int i=1;i<=m;i++)
    {
        read(opl);read(opr);
        if(T)opl^=ans,opr^=ans;
        ans=find(opr,1,M)-1;
        write(ans),P('\n');
    }
    return 0;
}
阅读更多
文章标签: 主席树 线段树
个人分类: 题解 线段树
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

JZOJ5710. 【北大夏令营2018模拟5.13】Mex

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭