【Codeforces】Divide by Zero 2018 and Codeforces Round #474 D-Full Binary Tree Que

192 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第20天,点击查看活动详情

题目链接

Problem - 960D - Codeforces

题目

You have a full binary tree having infinite levels.

Each node has an initial value. If a node has value xx, then its left child has value 2×x2\times x and its right child has value 2×x+12\times x + 1.

The value of the root is 1.

You need to answer qq queries.

There are 3 types of queries:

  1. Cyclically shift the values of all nodes on the same level as node with value xx by kk units. (The values/nodes of any other level are not affected).
  2. Cyclically shift the nodes on the same level as node with value xx by kk units. (The subtrees of these nodes will move along with them).
  3. Print the value of every node encountered on the simple path from the node with value xx to the root.

Positive kk implies right cyclic shift and negative kk implies left cyclic shift.

It is guaranteed that atleast one type 3 query is present.

题目大意

对于一颗有无限个节点的满二叉树,在最初如果一个节点的值为 xx,那么它的左孩子的值为 2×x2\times x,它的右孩子的值为 2×x+12\times x + 1,根的值为 1。

qq 个操作,每个操作为以下三种之一:

  1. 将与值为 xx 的节点处于同一层的所有节点循环移动 kk 个单位,任何其他级别的节点不受影响)。
  2. 将与值为 xx 的节点在同一层上的所有节点循环移动 kk 个单位,这些节点的子树将随之移动)。
  3. 输出从值为 kk 的节点到根的简单路径上遇到的每个节点的值。

k>0k>0 表示右循环移位,k<0k<0 表示左循环移位。

思路

开一个数组 dt[i]dt[i] 表示第 ii 层的数循环移动了 dt[i]dt[i] 个位置。

假设我们求出了 xx 位于第 tt 层,对于三种不同的操作:

  • 对于操作 2,它只会改变该层内部元素寻找父节点,对其他层找父节点的操作没有影响,我们只需要改变 dt[t]dt[t],记录该层在过去的基础上又移动了 kk 个单位。
  • 对于操作 1,它既改变该层内部元素寻找父节点,还改变了与其子节点在同一层的元素寻找父节点的答案。所以我们在让 dt[t]dt[t] 加上 kk 之后,需要消除对其子节点一层的移动,即让 dt[t+1]dt[t+1] 加上 2×k-2\times k
  • 对于操作 3,我们只需要从 xx 出发,先修正 xx 在本层中的位置,再寻找其父节点即可。

代码

#include <bits/stdc++.h>
using namespace std;
long long dt[61],bs[61];
int n;
void save(long long x,long long v)
{
	int t=0;
	while (x>=(1ll<<t+1)) t++;
	dt[t]+=v;
	dt[t]=(dt[t]%bs[t]+bs[t])%bs[t]; 
}
void load(long long x)
{
	int t=0;
	while (x>=(1ll<<t+1)) t++;
	while (1)
	{
		printf("%lld ",x);
		if (x==1) break;
		x+=dt[t];
		if (x>bs[t]*2-1) x-=bs[t];
		x/=2;
		t--;
	}
	printf("\n");
}
int main()
{
	scanf("%d",&n);
	bs[0]=1;
	for (int i=1;i<=60;++i)
		bs[i]=bs[i-1]*2;
	int opt;
	long long x,v;
	while (n--)
	{
		cin>>opt>>x;
		if (opt!=3) cin>>v;
		if (opt==1)
		{
			save(x,v);
			save(x*2,-v*2);
		}
		if (opt==2) save(x,v);
		if (opt==3) load(x);
	}
	return 0;
}