CodeForces 842D Vitya and Strange Lesson

题目大意:

N N 个数,a1,a2,,aN ; 有 M M 个询问,每次询问给出一个 x ,求当前数列异或(XOR) x x 之后的 mex 值( mex m e x 值指的是数列中未出现的最小自然数,例如 mex([5,1,1,0])=2 m e x ( [ 5 , 1 , 1 , 0 ] ) = 2 )。

Note : 每次询问之后原数列都会改变,即当前询问是基于上一个询问的。

题目链接:

CF-842-D

数据范围

1N,M3105 1 ≤ N , M ≤ 3 ⋅ 10 5

0ai31050x3105 0 ≤ a i ≤ 3 ⋅ 10 5 0 ≤ x ≤ 3 ⋅ 10 5

解题思路

首先,若有数列 {0,1,2,,2k1} { 0 , 1 , 2 , ⋯ , 2 k − 1 } ,且有整数 x x 满足 0x2k1 ,那么整个数列异或 x x 后的结果依旧是 {0,1,2,,2k1} ; 例如 {0,1,2,3} { 0 , 1 , 2 , 3 } 异或 2 之后得 {2,3,0,1} { 2 , 3 , 0 , 1 }

其次,异或( ) 满足结合律,即 (xy)z=x(yz) ( x ⊕ y ) ⊕ z = x ⊕ ( y ⊕ z ) ; 所以,不必照着题意改变原数列,只需将原数列 XOR 询问的前缀异或值就可以了。

2191=524287>3105, 2 19 − 1 = 524287 > 3 ⋅ 10 5 , 令 全集 S={0,1,2,,2191} S = { 0 , 1 , 2 , ⋯ , 2 19 − 1 } , A={a1,a2,,aN} A = { a 1 , a 2 , ⋯ , a N } (此处假设原数列已去重); A A 的补集令为 B ; 对于当前的询问 x x ,要求的 mex(A)=min(SAx) ; 又因为 Ax+Bx=(A+B)x=Sx=S A ⊕ x + B ⊕ x = ( A + B ) ⊕ x = S ⊕ x = S ,所以,所求的 mex(A)=min(SAx)=min(Bx) m e x ( A ′ ) = min ( S − A ⊕ x ) = min ( B ⊕ x )

综上,对于一个询问 x x , 就相当于在上述 B 集合中找一个值使得它 异或( ) x x 的值 最小;这种求 异或最值的问题 可以用 01Trie 树解决,将原数列 a 中没有出现的数(即上述的 B B ) 建成 01Trie 树,查询所要求的值即可。

Tips:要覆盖小于 3e5 3 e 5 的所有自然数,MaxN 要开到 6e5 6 e 5 (上面算过的),WA了好多发!

AC代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 600000;

int n, m;
bool flag[MaxN + 5];

struct Trie {
    int Node[20 * MaxN + 5][2], val[20 * MaxN + 5];
    int sz;
    void Init() {
        sz = 1;
        memset(Node[0], 0, sizeof(Node[0]));
        memset(val, 0, sizeof(val));
    }
    void insert(int x) {
        int cur = 0;
        for(int i = 20; i >= 0; i--) {
            int idx = (x >> i) & 1;
            if(!Node[cur][idx]) {
                memset(Node[sz], 0, sizeof(Node[sz]));
                Node[cur][idx] = sz;
                sz++;
            }
            cur = Node[cur][idx];
        }
        val[cur] = x;
    }
    int query(int x) {
        int cur = 0;
        for(int i = 20; i >= 0; i--) {
            int idx = (x >> i) & 1;
            if(Node[cur][idx])
                cur = Node[cur][idx];
            else cur = Node[cur][idx ^ 1];
        }
        return val[cur] ^ x;
    }
};

Trie t;

int main()
{
    while(scanf("%d %d", &n, &m) != EOF)
    {
        t.Init();
        memset(flag, 0, sizeof(flag));
        for(int i = 1; i <= n; i++) {
            int x;
            scanf("%d", &x);
            flag[x] = 1;
        }
        for(int i = 0; i <= MaxN + 1; i++) {
            if(flag[i] == 0)
                t.insert(i);
        }
        int num = 0;
        for(int i = 1; i <= m; i++) {
            int x;
            scanf("%d", &x);
            num = num ^ x;
            printf("%d\n", t.query(num));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值