• CF817E Choosing The Commander


    此题其实为 (01Trie) 模板题。

    看到加入删除求异或值, 我们不禁也必须想到 (01Trie) 树。(未学过的童鞋们除外

    1、什么是 (01Trie) ?

    首先我们要先知道什么是 (Trie) 树,这里推荐一篇个人认为还不错的博客

    顾名思义,所谓 (01 Trie), 是普通 (Trie) 树的一种特殊应用。与 (Trie) 树各个节点保存的都是字母类似,在 (01Trie) 上我们每个节点保存的是数字在 (2) 进制下的值所对应的 (01) 位。

    比如,对于一个数 (4), 其二进制下表示为 (1,0,0)。那么,我们就可以如下建树:

    1
    |
    0
    |
    0
    
    

    如果,我们又有一个数 (5), 其二进制表示为 (1,0,1), 那么插入之后,(01Trie) 就变为:

    1
    |
    0 
    | 
    0 1
    

    2、怎么应用?

    考虑如下问题: 给定 (n) 个数,求两两之间 (xor) 的最大值。那我们就可以对 (n) 个数建立一棵 (01Trie), 然后固定一个值,遍历一遍 (Trie) 树,在遍历时从高往低位贪心即可。

    3、关于此题

    (以下的“每一位”之类的言语均指二进制状态下)

    对于前两个操作,我们可以直接模仿普通的 (Trie) 树,记录 (sum) 为当前节点经过的数字数量,同时进行加减。在此不再赘述。

    对于第三个操作,如下考虑:

    1. 如果第 (i) 位可以为答案造成贡献,那么我们的 (l) 这一位一定要是 (1)
    2. 如果 (l) 这一位是 (1), 那我们肯定优先往 (x)(xor) 值那边跑,因为这样可以使得值更小。然后加上对应的值即可。
    3. 如果 (l) 的这一位是 (0) 呢? 那我们这一位肯定也只能是 (0)。所以 选择相等的那一边跑,一直到可以再次变小为止。

    上代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define N 1000010
    #define ll long long
    
    template <class T>
    inline void read(T& a){
    	T x = 0, s = 1;
    	char c = getchar();
    	while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
    	a = x * s;
    	return ;
    }
    
    struct tree{
    	int ch[2]; 
    	int sum;    // 经过这个节点的数的数量 
    	
    	tree(){
    		for(int i = 0; i < 2; i++)
    			ch[i] = 0;
    		return ; 
    	}
    } t[N * 5];
    
    int cnt = 1; 
    
    void build(int now, int x){
    	for(int i = (1 << 30); i; i >>= 1){
    		bool c = i&x;   // 获取第 i 位 0, 1 
    		t[now].sum++; 
    		if(!t[now].ch[c]) t[now].ch[c] = ++cnt; 
    		now = t[now].ch[c];
    	}
    	t[now].sum++; 
    	return ; 
    }
    
    void del(int now, int x){
    	for(int i = (1 << 30); i; i >>= 1){
    		bool c = i&x;   // 获取第 i 位 0, 1 
    		t[now].sum--; 
    		if(!t[now].ch[c]) t[now].ch[c] = ++cnt; 
    		now = t[now].ch[c]; 
    	}
    	t[now].sum--;
    	return ;  
    }
    
    int query(int now, int x, int k){
    	bool c1, c2; 
    	int ans = 0;
    	for(int i = 1 << 30; i; i >>= 1){
    		c1 = x&i, c2 = k&i; 
    		if(c1 < c2)
    			ans += t[t[now].ch[0]].sum; 
    		if(c1 && c2)
    			ans += t[t[now].ch[1]].sum; 
    		if(!c2)
    			now = t[now].ch[c1];
    		else now = t[now].ch[c1 ^ 1]; 
    	}
    	return ans; 
    }
    
    int main(){
    	int q; read(q);
    	while(q--){
    		int opt, x, k;
    		read(opt), read(x); 
    		if(opt == 1){
    			build(1, x); 
    		}
    		else if(opt == 2){
    			del(1, x); 
    		}
    		else{
    			read(k); 
    			printf("%d
    ", query(1, x, k)); 
    		}
    	}
    	return 0; 
    }
    
    
  • 相关阅读:
    System.Web.Security下提供了非常简单的MD5加密算法,但处在非Web层的项目要怎么使用MD5加密呢?
    切换用户账户访问共享 记
    从盛大通行证上摘下来的身份证验证代码
    HA_Xenu1.3.6 使用手记
    asp.net跨域共享session(不能跨主机名)
    应用程序池自动关闭
    忘记 win2003 密码 记
    Ajax 入门 【学习手记】
    转载 防盗链
    超链接的 target
  • 原文地址:https://www.cnblogs.com/wondering-world/p/14064237.html
Copyright © 2020-2023  润新知