Codeforces 339D Xenia and Bit Operations 题解

题目 英文原题

题目大意

给出一个长度为 2 n 2^n 2n 的序列 a a a,并进行 n n n 次更新操作
在第奇数次操作时,将每两个相邻的数 按位或,并将这些得到的值组成的序列代替序列 a a a
在第偶数数次操作时,将每两个相邻的数 按位异或,并将这些得到的值组成的序列代替序列 a a a

n n n 次操作后, a a a 序列中只剩下了一个数,我们称这个数为序列 a a a 的键值
给出 m m m 个询问,每次询问包含两个数 p , b p,b p,b,表示求当 a p = b a_p=b ap=b (其他数不变)时序列 a a a 的键值

解题思路

首先,我们要思考如何求出序列 a a a 的键值(先不考虑多组询问)

方法1:直接模拟
这个就没什么好说的了,按照题意 异或/按位或 相邻元素就好了。时间复杂度 O ⁡ ( 2 n + 1 − 1 ) \operatorname{O}(2^{n+1}-1) O(2n+11)

方法2:树形DP/dfs
n n n 次更新操作中出现的所有数组成一个节点数为 2 n + 1 − 1 2^{n+1}-1 2n+11 的满二叉树。其中,从下往上数第 i i i 层的节点代表第 i i i 次操作前序列 a a a 的状态,最后的答案就是根节点的值。时间复杂度 O ⁡ ( 2 n + 1 − 1 ) \operatorname{O}(2^{n+1}-1) O(2n+11)

虽然两个方法时间复杂度相同,但方法2在多组询问中更胜一筹

最开始,我们把初始序列 a a a 对应的满二叉树预处理出来
在每次询问前修改元素后(假如修改的元素位置为 x x x),我们发现有变化的节点都在 x x x 到根节点的路径上,并且这些节点的总数不超过 n n n
这样,每次询问时只需 O ⁡ ( n ) \operatorname{O}(n) O(n) 修改二叉树, O ⁡ ( 1 ) \operatorname{O}(1) O(1) 查询答案。总时间复杂度 O ⁡ ( ( 2 n + 1 − 1 ) + ( n m ) ) \operatorname{O}((2^{n+1}-1)+(nm)) O((2n+11)+(nm))

#include<cstdio>
#include<iostream>
using namespace std;
const long long Maxn=3000000+10;
long long a[Maxn],d[Maxn];
long long n,m;
void dfs(long long x) // dfs 预处理
{
	if(x>=(1<<(n+1)))return; // 注意:这种写法一定要判断边界,不然就无限递归了
	d[x]=d[x>>1]+1; // 计算深度,根节点的深度为 1
	dfs(x<<1);
	dfs(x<<1|1);
	if(a[x]!=-1)return; // 如果是叶子节点,就不需要合并子树信息
	if((n-d[x]) & 1)a[x]=(a[x<<1]^a[x<<1|1]); // 判断是异或还是按位或
	else a[x]=(a[x<<1]|a[x<<1|1]);
}
int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%lld%lld",&n,&m);
	for(long long i=1;i<(1<<(n+1));++i)
	a[i]=-1;
	for(long long i=(1<<(n+1))-(1<<n);i<(1<<(n+1));++i)
	scanf("%lld",a+i); // 给叶子节点赋值
	dfs(1);
	while(m--)
	{
		long long x,val;
		scanf("%lld%lld",&x,&val);
		x=(1<<(n+1))-(1<<n)+x-1; // 找到在树中对应的位置
		a[x]=val;
		while(x>1) // 更新祖先
		{
			x>>=1;
			if((n-d[x]) & 1)a[x]=(a[x<<1]^a[x<<1|1]);
			else a[x]=(a[x<<1]|a[x<<1|1]);
		}
		printf("%lld\n",a[1]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值