JZOJ 5710. 【北大夏令营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

Data Constraint

Data Constraint

Solution

  • 考虑用主席树的在线做法。

  • 主席树中以值域为下标,存的值为 min{max{posv}} m i n { m a x { p o s v } }

  • 即当前这个值最后出现的位置在哪里,所有位置的最小值。

  • 那么我们查询区间 [l,r] [ l , r ] 的时候,就查找第 rt[r] r t [ r ] 棵主席树,

  • 如果左区间的值 l ≥ l ,说明左区间所有值在询问区间中都出现过, 就不可能成为答案,

  • 于是就递归右区间,否则递归左区间。

  • 有个小优化:当读入的 ai>n a i > n 那它肯定不会成为答案,就干脆不加入了。

  • 动态开点,时空复杂度都是 O(N log N) O ( N   l o g   N )

Code

#include<cstdio>
#include<cctype>
using namespace std;
const int N=2e5+5;
struct data
{
    int p,l,r;
}f[N*19];
int tot,ans,num,pos;
int rt[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
void insert(int pre,int &v,int l,int r)
{
    f[v=++tot]=f[pre];
    if(l==r)
    {
        f[v].p=pos;
        return;
    }
    int mid=l+r>>1;
    if(num<=mid) insert(f[pre].l,f[v].l,l,mid); else insert(f[pre].r,f[v].r,mid+1,r);
    f[v].p=min(f[f[v].l].p,f[f[v].r].p);
}
int query(int v,int l,int r)
{
    if(l==r) return l;
    int mid=l+r>>1;
    if(f[f[v].l].p>=pos) return query(f[v].r,mid+1,r);
    return query(f[v].l,l,mid);
}
int main()
{
    freopen("mex.in","r",stdin);
    freopen("mex.out","w",stdout);
    int n=read(),m=read(),t=read();
    for(int i=1;i<=n;i++)
    {
        num=read();
        if(num<=n) insert(rt[i-1],rt[pos=i],0,n); else rt[i]=rt[i-1];
    }
    while(m--)
    {
        int l=read(),r=read();
        if(t) l^=ans,r^=ans;
        pos=l;
        ans=query(rt[r],0,n);
        write(ans),putchar('\n');
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值