• CF 979D Kuro and GCD and XOR and SUM(异或 Trie)


    CF 979D Kuro and GCD and XOR and SUM(异或 Trie)

    给出q(<=1e5)个操作。操作分两种,一种是插入一个数u(<=1e5),另一种是给出三个数x,k,s(<=1e5),求当前所有u中满足,k|u,x+u<=s,且(xoplus u)最大的u。

    做法好神啊。关于异或的问题有一种常见做法,就是利用01trie来查找在一堆数里面,哪个数与x的异或值最大。这道题就是这个思路。如果去掉k必须整除v这个条件,那么就转化成了上一个问题(只不过有最大值的限制,怎么解决具体看代码)。

    这道题的做法非常神奇。我们建1e5个Trie,第i个Trie中插入值为i的倍数的数。这样,查询x,k,s时,只要查询第k个Trie即可,因为里面的数一定满足k|v。插入时要遍历u的所有因数si,然后将u插入第si个Trie。

    注意,异或运算是在尾部对齐的,但是要在Trie上贪心,所以必须在插入和查询的数前补零,使他们长度相同。

    分析一波复杂度:

    • 预处理:我们需要预处理出u的因数。u的最大值为Max=1e5。用类似筛法的方法,时间复杂度是(O(Max(1+frac{1}{2}+frac{1}{3}...+frac{1}{Max}))=O(MaxInMax))
    • 查询:就是Trie上的查询,总时间复杂度为(O(qlog_2Max))
    • 插入:一个数最多只有(log_2(Max))个因数((2*10^9)内因数最多的数之一是1837836000,有1536个因数),所以总的时间复杂度为(O(qlog^2_2(Max)))
    • 空间复杂度:最多插入(qlog_2(Max))个数,因此空间复杂度为(qlog_2^2(Max))

    果然只能膜拜膜拜。

    #include <cstdio> 
    #include <vector>
    using namespace std;
    
    const int maxnum=1e5+5, maxq=1e5+5, maxn=maxq*17*17, INF=1e9;
    //maxnum指插入的数的最大值  maxq指查询的最多数目 
    //maxn指结点的最多数目(=maxq*插入几个trie*插入数的二进制长度)
    int s[maxn][2], minm[maxn], v[maxn], tot;
    int q, root[maxnum], use[maxnum];
    vector<int> div[maxnum];
    
    void init(){
    	for (int i=1; i<maxnum; ++i)
    		for (int j=i; j<maxnum; j+=i)
    			div[j].push_back(i);
    }
    
    //把x插到对应的trie里,注意维护子树中的最小数  l:处理到从左到右第几位 
    void insert(int &now, int x, int l){
    	if (!now){ now=++tot; minm[now]=INF; }
    	minm[now]=min(minm[now], x);
    	if (l==-1){ v[now]+=x; return; }
    	if ((x>>l)&1) insert(s[now][1], x, l-1);
    	else insert(s[now][0], x, l-1);
    }
    
    //要找到v<=lim,并且x^v尽量大(贪心)。函数返回v 
    //注意由于没有删除操作,路径底下一定有点。 
    int query(int now, int x, int lim, int l){  //l:第几位 
    	if (l==-1) return v[now];
    	int s0=s[now][0], s1=s[now][1];
    	if (!s0||minm[s0]>lim) return query(s[now][1], x, lim, l-1);
    	if (!s1||minm[s1]>lim) return query(s[now][0], x, lim, l-1);
    	if ((x>>l)&1) return query(s[now][0], x, lim, l-1);
    	else return query(s[now][1], x, lim, l-1);
    }
    
    int main(){
    	init();
    	scanf("%d", &q); int op, x, k, s;
    	for (int i=0; i<q; ++i){
    		scanf("%d", &op);
    		if (op==1){
    			scanf("%d", &x); 
    			if (use[x]) continue; use[x]=1;
    			for (int j=0; j<div[x].size(); ++j)
    				insert(root[div[x][j]], x, 18);
    		} else {
    			scanf("%d%d%d", &x, &k, &s);
    			if (x%k||!minm[root[k]]||minm[root[k]]+x>s) puts("-1");  //注意可能没有一个数 
    			else printf("%d
    ", query(root[k], x, s-x, 18));  //保证一定有解 
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    JAVA 框架
    npm安装超时,使用淘宝镜像
    使用vite搭建Vue3项目
    前端常用框架
    vue发布自己的组件库-vue3
    vue2升级vue3-基础教程
    Navicat Premium 15破解失败解决方案
    大屏
    vue使用高德地图
    vue生命周期及钩子函数
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/9047757.html
Copyright © 2020-2023  润新知