LOJ 517 计算几何瞎暴力

一道有意思的题目。
首先注意到这些操作都不是区间修改,所以可以用全局的东西来做。考虑每一次排序之后,可能会有一些加入操作以及异或操作,然后整个序列就是一些之前排好了序的,一些之后加入的。我们考虑维护之前排好序的,用一个trie就行了,维护之后加入的,直接一个数组就好了。然后考虑操作:加入操作不会影响trie,直接在数组中加入就好了。异或操作,不会改变数组的顺序,打一个标记。排序的话,我们先把所有没加入的数都加进去,然后考虑标记就行了。
这样讲可能有些难以讲懂,稍微解释一下。
如果没有push_back 操作,只有全局排序以及全局异或该怎么做?
我们可以记录一个trie,然后记录一下全局异或了多少,比如是allxor,然后我们询问的实际上就是当前第几大的数之类东西。那么考虑如何求这个东西,我们还是像一般的trie二分那样,但是因为有了全局异或的标记,所以比如现在到了第i位,如果allxor第i位是0,那么没有影响,否则0变成1,1变成0。
如果有了push_back操作,那么直接暴力记录一下就好了。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int ls[N*30] , rs[N*30] , val[N*30][30] , sum[N*30];
int len , tcnt , nlen , txor , allxor , root;

int pre[N][30] , a[N];

void modify(int &o,int l,int r,int val)
{
	if (!o) o = ++tcnt;
	sum[o]++;
	for (int i = 0;i < 30;i ++) if (val & (1 << i) ) ::val[o][i]++;
	if (l == r) return;
	int mid = l + r >> 1;
	if (val <= mid) modify(ls[o] , l , mid , val);
	else modify(rs[o] , mid + 1, r , val);
}

ll query(int o,int l,int r,int rnk,int dep = 29)
{
	if (!o || !rnk) return 0;
	if (l == r) return 1ll * (l ^ allxor) * rnk;
	int mid = l + r >> 1;
	if ( txor & (1 << dep) )
	{
		if (rnk <= sum[ rs[o] ] ) return query(rs[o] , mid + 1  , r , rnk , dep - 1);
		ll ans = 0;
		for (int i = 0;i < 30;i++)
		{
			if ( allxor & (1 << i) ) 
			{
				ans += ( 1ll * ( sum[ rs[o] ] - val[ rs[o] ][i] ) ) << i;
			}else
			{
				ans += ( 1ll * val[rs[o]][i] ) << i;
			}
		}
		return ans + query( ls[o] , l , mid , rnk - sum[rs[o]] , dep - 1);
	}else
	{
		if (rnk <= sum[ ls[o] ] ) return query(ls[o] , l  , mid , rnk , dep - 1);
		ll ans = 0;
		for (int i = 0;i < 30;i++)
		{
			if ( allxor & (1 << i) ) 
			{
				ans += ( 1ll * (sum[ ls[o] ] - val[ ls[o] ][i] ) ) << i;
			}else
			{
				ans += ( 1ll * val[ls[o]][i] )<< i;
			}
		}
		return ans + query( rs[o] , mid + 1, r , rnk - sum[ls[o]] , dep - 1);
		
	}
}


ll query(int l,int r)
{
	ll ans = 0;
	if (r > nlen)
	{
		for (int i = 0;i < 30;i++)
		{
			int cal = pre[r][i] - pre[ max( nlen , l - 1 ) ][i] ;
			if ( allxor & (1 << i) ) 
			{
				ans += 1ll * (r - max(l - 1 , nlen ) -  cal) << i;
			}else
			{
				ans += ( 1ll * cal )<< i;
			}
		}
	}
	
	if (l <= nlen)	ans += query(root , 0 , ( 1 << 30 )- 1, min(r , nlen ) ) - query(root , 0 ,( 1 << 30 ) - 1, l - 1);
	return ans;
}

int n , m;
int main()
{
	scanf("%d",&n);
	for (int i = 1;i <= n;i++)
	{
		scanf("%d",&a[++len]);
		for (int j = 0;j < 30;j ++) pre[len][j] = pre[len - 1][j] + ( ( a[len] & (1 << j) ) ? 1 : 0 );
	}
	
	scanf("%d",&m);
	while (m--)
	{
		int typ;scanf("%d",&typ);
		if (typ == 1)
		{
			scanf("%d",&a[++len]);
			a[len] ^= allxor;
			for (int j = 0;j < 30;j ++) pre[len][j] = pre[len - 1][j] + ( ( a[len] & (1 << j) ) ? 1 : 0 );
		}else
		if (typ == 2)
		{
			int l ,r;scanf("%d%d",&l,&r);
			printf("%lld\n",query(l , r) );
		}else
		if (typ == 3)
		{
			int x;scanf("%d",&x);
			allxor ^= x;
		}else
		{
			for (int i = nlen + 1;i <= len ;i++) modify(root , 0 , ( 1 << 30 ) - 1 , a[i] );
			txor = allxor;nlen = len;
		}
	}
	
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值