• [BZOJ 4103] [Thu Summer Camp 2015] 异或运算 【可持久化Trie】


    题目链接:BZOJ - 4103

    题目分析

    THUSC滚粗之后一直没有写这道题,从来没写过可持久化Trie,发现其实和可持久化线段树都是一样的。嗯,有些东西就是明白得太晚。

    首先Orz ZYF-ZYF 神犇的题解。

    题目给出的 n 和 m 的范围差别很大,n 很小,m 很大,因此可以想到 n 的范围是为了直接暴力枚举。

    题目要求的就是 A 的一段区间中的数和 B 的一段区间中的数的异或的第 k 大值。

    位运算有关的题目,一般是从高位到低位贪心之类的。

    区间异或,一般要使用可持久化 Trie。

    我们对于范围大的 B 数组建立可持久化 Trie,这样就可以提取 B 数组的一个区间了。

    从高位到低位,枚举 A 数组区间中的每个元素,根据 Trie 结点的信息,求出这一位为 0 和 为 1 的各有多少,并据此确定答案的这一位。

    要注意的是,A 数组区间中每个元素要对应的 Trie 结点是不同的,由它们的前几位确定(因为它们的前几位不同,但是要求异或之后前几位相同)。

    编辑完这篇博客之后还是一个一个地添加了标签,虽然这个blog马上就要停更了,感觉这几天写的任何一篇都有可能是最后一篇。

    奇怪的感觉。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxN = 1000 + 5, MaxM = 300000 + 5, MaxNode = 10000000 + 5;
    
    int n, m, p, Index, Ans;
    int A[MaxN], B[MaxM], Root[MaxM], Son[MaxNode][2], T[MaxNode], F[MaxN], Q[MaxN];
    
    bool OK[MaxN];
    
    void Build(int &x, int y, int Num, int Bit)
    {
    	if (!x) x = ++Index;
    	if (Bit == 0)
    	{
    		T[x] = T[y] + 1;
    		return;
    	}
    	if (Num & (1 << (Bit - 1)))
    	{
    		Son[x][0] = Son[y][0];
    		Build(Son[x][1], Son[y][1], Num, Bit - 1);
    	}
    	else
    	{
    		Son[x][1] = Son[y][1];
    		Build(Son[x][0], Son[y][0], Num, Bit - 1);
    	}
    	T[x] = T[Son[x][0]] + T[Son[x][1]];
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; ++i) scanf("%d", &A[i]);
    	for (int i = 1; i <= m; ++i) 
    	{
    		scanf("%d", &B[i]);
    		Build(Root[i], Root[i - 1], B[i], 31);
    	}
    	scanf("%d", &p);
    	int u, d, l, r, kk;
    	for (int i = 1; i <= p; ++i)
    	{
    		scanf("%d%d%d%d%d", &u, &d, &l, &r, &kk);
    		kk = (d - u + 1) * (r - l + 1) - kk + 1; 
    		Ans = 0;
    		for (int j = u; j <= d; ++j)
    		{
    			F[j] = Root[r];
    			Q[j] = Root[l - 1];
    		}
    		for (int j = 30; j >= 0; --j)
    		{
    			int x, Temp = 0;
    			for (int k = u; k <= d; ++k)
    			{
    				x = (A[k] & (1 << j));
    				if (x) Temp += T[Son[F[k]][1]] - T[Son[Q[k]][1]];
    				else Temp += T[Son[F[k]][0]] - T[Son[Q[k]][0]];
    			}
    			if (Temp >= kk)
    			{
    				for (int k = u; k <= d; ++k)
    				{
    					x = (A[k] & (1 << j));
    					if (x) 
    					{
    						F[k] = Son[F[k]][1];
    						Q[k] = Son[Q[k]][1];
    					}
    					else
    					{
    						F[k] = Son[F[k]][0];
    						Q[k] = Son[Q[k]][0];
    					}
    				}
    			}
    			else
    			{
    				kk -= Temp;
    				Ans |= (1 << j);				
    				for (int k = u; k <= d; ++k)
    				{
    					x = (A[k] & (1 << j));
    					if (x) 
    					{
    						F[k] = Son[F[k]][0];
    						Q[k] = Son[Q[k]][0];
    					}
    					else
    					{
    						F[k] = Son[F[k]][1];
    						Q[k] = Son[Q[k]][1];
    					}
    				}
    			}
    		}
    		printf("%d
    ", Ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    海量数据中,寻找最小的k个数。
    快速排序
    反转一个单链表,分别以迭代和递归的形式来实现
    N个大小不等的自然数排序,时间复杂度为O(n),空间复杂度为O(1)
    堆排序
    两个已经排好序的链表合并为一个有序链表
    字符串过滤空格、回车、tab
    求一个浮点数的连续子序列最大乘积 (2013 小米校园招聘笔试题)
    单向循环链表队列,从头开始报数,当报到m或者m的倍数的元素出列
    给一个数组,元素都是整数(有正数也有负数),寻找连续的元素相加之和为最大的序列。
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4638279.html
Copyright © 2020-2023  润新知