【NOIP模拟】board(线段树维护二进制,树序号化为二进制)
SOURCE:NOIP2016-RZZ-2 T3
题目描述
给出这样一棵“二叉树”:
-
- 每个节点有左右两个儿子,并如下定义每个节点的高度:假设父亲节点的高度为 h ,那么他的两个儿子的节点的高度都是 h + 1 ,相同高度的所有节点称作一层。
- 每个节点的左儿子的子树都在右儿子的子树的左边,每一层相邻的两个节点之间有一条边。
下面是一个例子:
每一条图上的路径用一个字符串表示,字符串中的每一个字符表示一个移动。字符仅包含如下五种:
-
- 1:表示移动到当前节点的左儿子
- 2:表示移动到当前节点的右儿子
- U:表示移动到当前节点的父亲节点
- L:表示移动到当前节点同层的左边的节点(保证当前节点在这一层中不是最左边的节点)
- R:表示移动到当前节点同层的右边的节点(保证当前节点在这一层中不是最右边的节点)
用一条路径来表示这条路径的终点,例如路径:221LU 就表示上图中的节点 A 。
给出两条路径,你的任务是求出着两条路径的终点之间的最短路。
输入格式
输入两行,每行一个字符串,分别表示两条路径。
输出格式
输出一行一个整数,表示能得到的串的总数。
样例数据 1
输入
221LU
12L2
输出
3
备注
【数据规模与约定】
用 D 表示所有经过的节点中,深度最大的节点的深度;S 表示输入字符串的最大长度。对于 10% 的数据,D≤10。对于 30% 的数据,D≤50。
对于 50% 的数据,D≤1000。
对于 70% 的数据,D≤20000。
对于 100% 的数据,D≤100000;S≤100000。
【题目分析】
难题一:找到两个点的终点。
因为路径可能很长,十进制数很容易炸,所以考虑将树的编号用二进制表示:
向左儿子走二进制数后面加$0$, 右儿子加$1$, 向父亲走删除二进制数最后的数字。这些都好实现, 不过在向左时我们需要给二进制数加$1$(右减$1$),此时便涉及到进位。
【线段树维护二进制数】
将二进制数建成一颗线段树,这样在加$1$时,我们只需找到第一个$0$将它修改为$1$, 并把此前的$1$全部修改为$0$即可(减$1$反之)
查找第一个$x(0 / 1)$时,我们先在右儿子中查找(保证最前),若未找到则再在左儿子中找。
将最后的二进制数导出,我们就知道此时点到根的路径了(不走横向边)。
难题2:怎样才是最短路?
画图观察即可知,最短路径要求两点至少得在同一深度,则先将深度较大的点上移至同一深度,$ans += |dep_{i} - dep_{j}|$
这时再加上他们中间的距离即可。但是如果再同时往上跳,他们间的距离可能更小T_T,这时就需要从根节点开始,分别沿着从难题一中导出的路径向下找出剩余距离的最短值。
可以发现,若当前层距离为$dis$,则下一层的距离会根据两点走的方向发生变化:
$or$
$dis = dis × 2$
$dis = dis × 2 - 1$
$dis = dis × 2 + 1$
最后$ans += min\{dis + 2 × (dep - i)\} (其中dep为终点深度较小的深度, i为跳至的深度)$
具体可以看代码。
【code】

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<algorithm> #include<vector> using namespace std; const int D = 100005, S = 100005; char s[S], t[S]; int ls, lt, ans, ret; struct node{ int len, sum, tag; node():len(0), sum(0), tag(0){} }; namespace SegTree{ node tr[D * 4]; inline void upt( int k){ int lc = k << 1, rc = k << 1 | 1; tr[k].sum = tr[lc].sum + tr[rc].sum; } inline void cover( int k, int v){ tr[k].sum = (v - 1) * tr[k].len; tr[k].tag = v; } inline void pushDown( int k){ int lc = k << 1, rc = k << 1 | 1; if (tr[k].tag){ cover(lc, tr[k].tag); cover(rc, tr[k].tag); tr[k].sum = (tr[k].tag - 1) * tr[k].len; tr[k].tag = 0; } } inline void build( int k, int l, int r){ tr[k].len = r - l + 1; if (l == r) return ; int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1; build(lc, l, mid); build(rc, mid + 1, r); upt(k); } inline void modify( int k, int l, int r, int x, int y, int v){ if (x <= l && r <= y){ cover(k, v); return ; } pushDown(k); int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1; if (x <= mid) modify(lc, l, mid, x, y, v); if (y > mid) modify(rc, mid + 1, r, x, y, v); upt(k); } inline int query_1( int k, int l, int r, int pos){ if (pos < l) return 0; if (tr[k].sum == 0) return 0; if (l == r) return tr[k].sum == 1 ? l : 0; int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1; pushDown(k); int ret = 0; ret = query_1(rc, mid + 1, r, pos); if (!ret) ret = query_1(lc, l, mid, pos); return ret; } inline int query_0( int k, int l, int r, int pos){ if (pos < l) return 0; if (tr[k].sum == tr[k].len) return 0; if (l == r) return tr[k].sum == 0 ? l : 0; pushDown(k); int ret = 0; int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1; ret = query_0(rc, mid + 1, r, pos); if (!ret) ret = query_0(lc, l, mid, pos); return ret; } inline void treeExport( int k, int l, int r, char *p){ if (l == r){ p[l] = tr[k].sum + '0' ; // if(l == 1) cout<<"!!!!!!!"<<p[l]<<endl; return ; } pushDown(k); int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1; treeExport(lc, l, mid, p); treeExport(rc, mid + 1, r, p); } } using namespace SegTree; inline void loadIn( char *f, int &q){ static char p[S]; scanf ( "%s" , p + 1); int maxx = strlen (p + 1); q = 0; memset (tr, 0, sizeof tr); build(1, 1, maxx); for ( int i = 1; i <= maxx; i++){ switch (p[i]){ case '1' :{ q++; modify(1, 1, maxx, q, q, 1); } break ; case '2' :{ q++; modify(1, 1, maxx, q, q, 2); } break ; case 'U' :{ q--; } break ; case 'R' :{ int first_0 = query_0(1, 1, maxx, q); modify(1, 1, maxx, first_0, first_0, 2); modify(1, 1, maxx, first_0 + 1, q, 1); } break ; case 'L' :{ int first_1 = query_1(1, 1, maxx, q); modify(1, 1, maxx, first_1, first_1, 1); modify(1, 1, maxx, first_1 + 1, q, 2); } break ; default : break ; } } treeExport(1, 1, maxx, f); } inline int Re(){ int i = 0, f = 1; char ch = getchar (); for (; (ch < '0' || ch > '9' ) && ch != '-' ; ch = getchar ()); if (ch == '-' ) ch = getchar (), f = -1; for (; ch >= '0' && ch <= '9' ; ch = getchar ()) i = (i << 3) + (i << 1) + (ch - '0' ); return i * f; } inline void Wr( int x){ if (x < 0) putchar ( '-' ), x = -x; if (x > 9) Wr(x / 10); putchar (x % 10 + '0' ); } int main(){ loadIn(s, ls), loadIn(t, lt); ans = abs (ls - lt); int n = min(ls, lt), dis = 0; ret = 2 * n; for ( int i = 1; i <= n; i++){ if (s[i] == t[i]) dis = dis * 2; else if (s[i] == '0' && t[i] == '1' ) dis = 2 * dis + 1; else if (s[i] == '1' && t[i] == '0' ){ dis = 2 * dis - 1; if (dis < 0) dis = -dis, swap(s, t); } if (dis > ret) break ; ret = min(ret, dis + 2 * (n - i)); } // cout<<s+1<<" "<<t + 1<<endl; // Wr(ans), putchar(' '); Wr(ans + ret), putchar ( '\n' ); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步