2024杭电ACM-个人PK赛(2)C XOR-Sum

Problem - 7420 (hdu.edu.cn)

很典的一道题,比赛的时候最后不太想写了,赛后看了一下还是挺简单的

每次的查询可以转化为 \sum a[nm]*[(nm\oplus i)<=x]

这个东西显然是可以用trie树维护的

大概讲一下维护的方法

trie树每个点会记录它的整个子树的数值和

我们从高位向低位枚举

如果这一位上x是0,那么nm和i这一位必须相同

如果这一位上x是1,那么如果nm和i这一位可以相同(异或之后这一位变成0),那么整个子树的值都满足条件,我们将这一部分加到ans里。然后如果存在nm和i这一位可以不同的情况,我们继续往下枚举。

最后如果枚举到i==0,这时说明出现了[(nm\oplus i)==x]的情况,这种情况当然也是符合题意的,我们加上这部分贡献。

修改直接从高位到低位枚举每次减去原来的数值加上修改后的数值即可

(据说这个题也可以用线段树嗯造,但是我不太会)

#define  _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<algorithm> 
#include<string.h>
#include<iostream>
#include<queue>
#include<math.h>
#include<string>
#include<map>
#include<functional>
#include<unordered_map>
#include<bitset>
#include<set>

using namespace std;

#define int long long
#define inf 0x3f3f3f3f
#define N 16252928

int a[N];

int nn, q;

int ii, x;

int trie[N][2];
int vals[N];

int cnt = 0;

void insert(int nm, int val)
{
	int idx = 0;

	for (int i = 31; i >= 0; i--)
	{
		if (nm&(1 << i))
		{
			if (trie[idx][1])
			{
				idx = trie[idx][1];
				vals[idx] += val;
			}
			else
			{
				trie[idx][1] = ++cnt;
				idx = trie[idx][1];
				vals[idx] += val;
			}
		}
		else
		{
			if (trie[idx][0])
			{
				idx = trie[idx][0];
				vals[idx] += val;
			}
			else
			{
				trie[idx][0] = ++cnt;
				idx = trie[idx][0];
				vals[idx] += val;
			}
		}
	}

}


void change(int nm, int preval, int val)
{
	int idx = 0;

	for (int i = 31; i >= 0; i--)
	{
		idx = trie[idx][bool((1 << i)&nm)];

		vals[idx] -= preval;

		vals[idx] += val;
	}
}

int ex(int X, int ii)
{
	int res = 0;

	int idx = 0;

	for (int i = 31; i >= 0; i--)
	{
		if ((1 << i)&x)
		{
			if ((1 << i)&ii)
			{
				if (trie[idx][1])
				{
					res += vals[trie[idx][1]];
				}

				if (!trie[idx][0])break;

				idx = trie[idx][0];


			}
			else
			{
				if (trie[idx][0])
				{
					res += vals[trie[idx][0]];
				}

				if (!trie[idx][1])break;

				idx = trie[idx][1];
			}

		}
		else
		{
			if ((1 << i)&ii)
			{
				if (!trie[idx][1])break;

				idx = trie[idx][1];
			}
			else
			{

				if (!trie[idx][0])break;

				idx = trie[idx][0];

			}
		}


		if (i == 0)res += vals[idx];

	}

	return res;


}



signed main(void)
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	cin >> nn >> q;

	for (int i = 0; i < nn; i++)
	{
		cin >> a[i];

		insert(i, a[i]);
	}




	for (int i = 1; i <= q; i++)
	{
		int op;

		cin >> op;

		cin >> ii >> x;

		if (op == 1)
		{
			change(ii, a[ii], x);

			a[ii] = x;
		}
		else
		{
			int ans = ex(x, ii);

			cout << ans << '\n';
			
		}
	}

	
	system("pause");
	return 0;
}

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值