---
title: Paths in a Complete Binary Tree
tags:
  - Bitmask
id: "388"
categories:
  - - OI
    - Common Skill
date: 2017-06-15 20:30:22
---

## 题目描述

$T$ is a complete binary tree consisting of $n$ vertices. It means that exactly one vertex is a root, and each vertex is either a leaf (and doesn't have children) or an inner node (and has exactly two children). All leaves of a complete binary tree have the same depth (distance from the root). So $n$ is a number such that $n+1$ is a power of $2$.

In the picture you can see a complete binary tree with $n=15$.

![](1.png)

Vertices are numbered from $1$ to $n$ in a special recursive way: we recursively assign numbers to all vertices from the left subtree (if current vertex is not a leaf), then assign a number to the current vertex, and then recursively assign numbers to all vertices from the right subtree (if it exists). In the picture vertices are numbered exactly using this algorithm. It is clear that for each size of a complete binary tree exists exactly one way to give numbers to all vertices. This way of numbering is called symmetric.

You have to write a program that for given $n$ answers $q$ queries to the tree.

Each query consists of an integer number $u_i$ and a string $s_i$, where $u_i$ is the number of vertex, and $s_i$ represents the path starting from this vertex. String $s_i$ doesn't contain any characters other than $L, R$ and $U$, which mean traverse to the left child, to the right child and to the parent, respectively. Characters from $s_i$ have to be processed from left to right, considering that $u_i$ is the vertex where the path starts. If it's impossible to process a character (for example, to go to the left child of a leaf), then you have to skip it. The answer is the number of vertex where the path represented by $s_i$ ends.

For example, if $u_i=4$ and $s_i=UURL$, then the answer is $10$.

## 题意概述

给定一棵有$n$个节点的按中序遍历编号的完全二叉树、起始点的编号$u$以及只由$L, R$和$U$组成的操作序列$s$（其中$L$表示走到左儿子，$R$表示走到右儿子，$U$表示走到父亲），求终止点的编号。

数据范围：$1 \le n \le 10^{18}, \; 1 \le u_i \le n, \; 1 \le |s_i| \le 10^5$。

## 算法分析

设当前走到的数为$p$。将树中的数用二进制表示，可以得到下图：

![](2.png)

如果$p={n+1 \over 2}$，那么$U$操作将无效。如果$p \equiv 1 \pmod 2$，那么$L, R$操作将无效。

观察此图可以发现，进行一次$U$操作相当于将$p$最低位的$1$左移一位（如果前一位已经是$1$则只需将这一位变成$0$）；进行一次$L$操作相当于将最低位的$1$右移一位；进行一次$R$操作相当于将最低位的$1$的后一位变成$1$。

证明如下：

> 这是一棵中序遍历的完全二叉树。每个节点的编号等于不在它右边的节点个数（如果你把树画的足够标准的话）。
>
> 设树的深度为$d$，节点$i$的深度为$d_i$。定义节点$i$的高度$h_i=d-d_i$。显然，节点$i$两棵子树的大小均为$s_i=2^{h_i}-1$。定义$lowbit_i$等于$i$的二进制表示中最低位的$1$所表示的数。可以发现$lowbit_i=2^{h_i}$。那么$s_i=lowbit_i-1$。
>
> 设数字$i$进行一次操作后的数字为$j$。
>
> 对于$L$操作：
>
> $\because j$的右子树在$j$的右边，且在$i$的左边.
>
> $\therefore (i-1)-j=s_j$.
>
> $j=i-lowbit_j$.
>
> 又$\because h_i=h_j+1$.
>
> $\therefore lowbit_i=2lowbit_j$.
>
> $j=i-{lowbit_i \over 2}$.
>
> 这等价于将最低位的$1$右移一位。
>
> 对于$R$操作：
>
> $\because j$的左子树在$j$的左边，且在$i$的右边.
>
> $\therefore (j-1)-i=s_j$.
>
> $j=i+lowbit_j$.
>
> 又$\because h_i=h_j+1$.
>
> $\therefore lowbit_i=2lowbit_j$.
>
> $j=i+{lowbit_i \over 2}$.
>
> 这等价于将最低位的$1$的后一位变成$1$。
>
> 对于$U$操作：
>
> ①$i$在$j$左边.
>
> $\because$等价于$L$操作的逆操作.
>
> $\therefore i=j-lowbit_i$.
>
> $j=i+lowbit_i$.
>
> ②$i$在$j$右边.
>
> $\because$等价于$R$操作的逆操作.
>
> $\therefore i=j+lowbit_i$.
>
> $j=i-lowbit_i$.
>
> 这等价于将$p$最低位的$1$左移一位。

由此得证。

## 代码

```cpp
#include <iostream>
using namespace std;
long long n, t, p, len;
string s;
long long lowbit(long long t) {
    return t & -t;
}
int main()
{
    cin >> n >> t;
    while (t--) {
        cin >> p >> s;
        len = s.length();
        for (int i = 0; i < len; ++i) {
            long long q = lowbit(p);
            switch (s[i]) {
                case 'U': {
                    if (p != n + 1 >> 1) {
                        p -= q;
                        p |= q << 1;
                    }
                    break;
                }
                case 'L': {
                    if (!(p & 1)) {
                        p -= q >> 1;
                    }
                    break;
                }
                case 'R': {
                    if (!(p & 1)) {
                        p += q >> 1;
                    }
                    break;
                }
            }
        }
        cout << p << endl;
    }
    return 0;
}
```
