• CodeForces 665E. Beautiful Subarrays(字典树)(贪心)(异或前缀和)


    题意:给定一个长度为n(1 <= n <= 1e6)的数组a[i](0 <= a[i] <= 1e9)和k(1 <= k <= 1e9)。求有多少个区间[l, r]是合法的。我们认为一个区间是合法的,当且仅当(a[l]oplus a[l + 1]oplus a[l + 2] oplus ... a[r] >= k)

    分析:对于一个区间是否合法,我们可以先求出异或的前缀和,对于一个区间[l, r]合法,代表者(sum[l - 1] oplus sum[r] >= k),我们枚举每个当前的前缀和,然后用一个数据结构(trie树)维护,查询之前多有少个前缀和和当前的前缀和异或起来>=k,然后我们分析一下,如何在查询的时候求出和当前(sum[r])异或起来大于>=k的前缀和。我们从根节点出发,从每个数的二进制最高位开始统计,比较sum[r]和k的最高位,如果k的这一位代表0,我们就直接累加起来和sum[r]异或起来当前为1的那棵子树中存在的前缀和数量,即(+cnt[tr[p][!u]]),u表示sum[r]当前位的数,那么不管之后怎么走,异或起来都大于k,然后我们再统计和当前sum[r]异或起来为0的前缀和数量,直接继续走就可以,因为这棵子树中还存在一些前缀和,和当前前缀和异或起来>=k的数。然后当k的这一位等于1的时候,我们无法直接判断,直接继续走就可以。

    字典树的结点个数要开多少呢,可以看出(a[i]in{[0, 1e9]})(log(1e9) == 30),那么我们可以开(1e6 * 30 = 3e7)个结点。

    字典树不仅能在叶子结点维护插入的数的数量,而且能在每个结点上累加,代表这里曾今被插过的数的数量。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    using LL = long long;
    const int N = 30 * 1000005;
    const int M = 1000005;
    int tr[N][2], idx;
    int cnt[N];
    int a[M];
    int sum[M];
    
    int n, k;
    
    void insert(int val)
    {
    	int p = 0;
    	for (int i = 30; i >= 0; --i)
    	{
    		int u = (val >> i) & 1;
    		if (!tr[p][u]) tr[p][u] = ++idx;
    		p = tr[p][u];
    		++cnt[p];
    	}
    }
    
    int query(int val)
    {
    	int p = 0, sum = 0;
    	for (int i = 30; i >= 0; --i)
    	{
    		int u = (val >> i) & 1;
    		int c = (k >> i) & 1;
    		if (c == 0)
    		{
    			int k = tr[p][!u];
    			sum += cnt[k];
    			if (tr[p][u] == 0) return sum;
    			p = tr[p][u];
    		}
    		else
    		{
    			p = tr[p][!u];
    			if (p == 0) return sum;
    		}
    	}
    	return sum + cnt[p];
    }
    
    int main()
    {
    	scanf("%d%d", &n, &k);
    	for (int i = 1; i <= n; ++i)
    	{
    		scanf("%d", &a[i]);
    		sum[i] = sum[i - 1] ^ a[i];
    	}
    
    	LL res = 0;
    	for (int i = 1; i <= n; ++i)
    	{
    		res += query(sum[i]);
    		insert(sum[i]);
    	}
    
    	for (int i = 1; i <= n; ++i)
    	{
    		if (sum[i] >= k) ++res;
    	}
    
    	printf("%lld
    ", res);
    
    	return 0;
    }
    
  • 相关阅读:
    TranslateAnimation类:位置变化动画类
    android 启动第三方程序的代码(利用ComponentName)
    android背景选择器总结
    Spinner控件怎样在subActivity中使用
    Android 自定义 spinner (背景、字体颜色)
    AndroidWebView
    android gravity属性和weight属性
    android得到控件在屏幕中的位置
    解决android自定义标题充满的问题
    android中ListView的常用样式和属性
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13285215.html
Copyright © 2020-2023  润新知