给出这样一棵“二叉树”:
每个节点有左右两个儿子,并如下定义每个节点的高度:假设父亲节点的高度为 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。
解题思路
当确定了两个节点后,最短路就一定是两个节点向上移动到同一高度,然后再沿着这一层中间的边走过去,那么只要从根节点枚举这两个节点向上跳到那一层即可。(不一定是节点较浅的所在层,先往上跳可能更近,从根节点条避免高精度);
由于二叉树上的点序号有lc=fa*2,rc=fa*2+1,且同层为[2k,2k+1−1]这段连续的数,所以可以开两个二进制数组,向坐儿子做末尾加0,向右儿子走末尾加1,但水平移动时涉及进退位,有可能被卡,所以可用一颗线段树来维护二进制数组,这样时间复杂度为O(n∗log2n).详见代码。
这里写代码片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=1e5+5;
struct tree
{
int lazy,sum,len;
}tr[N<<2];
int s[N],t[N],sn,tn;
void build(int l,int r,int now)
{
tr[now].len=r-l+1;
if(l==r)return;
int mid=l+r>>1;
build(l,mid,now<<1);
build(mid+1,r,now<<1|1);
}
void add_lazy(int now ,int lazy)
{
tr[now].lazy=lazy;
tr[now].sum=(lazy-1)*tr[now].len;
}
void pushdown(int now)
{
if(tr[now].lazy)
{
add_lazy(now<<1,tr[now].lazy);
add_lazy(now<<1|1,tr[now].lazy);
tr[now].lazy=0;
}
}
void update(int now)
{
tr[now].sum=tr[now<<1].sum+tr[now<<1|1].sum;
}
void modify(int l,int r,int now,int x,int y,int z)
{
if(x<=l&&r<=y)return add_lazy(now,z);
int mid=l+r>>1;
pushdown(now);
if(x<=mid)modify(l,mid,now<<1,x,y,z);
if(y>mid)modify(mid+1,r,now<<1|1,x,y,z);
update(now);
}
int query_l(int l,int r,int now,int x)//进位找连续的1打标记 ,这里找的是从x位往前第一个0.
{
if(x<l)return 0;
if(tr[now].sum==tr[now].len)return 0;
if(l==r)return tr[now].sum==0?l:0;
int mid=l+r>>1;
pushdown(now);
int ret=query_l(mid+1,r,now<<1|1,x);
if(!ret)ret=query_l(l,mid,now<<1,x);
return ret;
}
int query_r(int l,int r,int now,int x)//退位找连续的0打标记 ,这里找的是从x位往前第一个1.
{
if(x<l)return 0;
if(tr[now].sum==0)return 0;
if(l==r)return tr[now].sum==1?l:0;
int mid=l+r>>1;
pushdown(now);
int ret=query_r(mid+1,r,now<<1|1,x);
if(!ret)ret=query_r(l,mid,now<<1,x);
return ret;
}
void tree_export(int l,int r,int now,int x,int *p)
{
if(l==r)
{
p[l]=tr[now].sum;
return;
}
int mid=l+r>>1;
pushdown(now);
tree_export(l,mid,now<<1,x,p);
tree_export(mid+1,r,now<<1|1,x,p);
}
void read(int *p,int &n)
{
static char s[N];
scanf("%s",s+1);
int mx=strlen(s+1);
build(1,mx,1);
n=0;
for(int i=1;i<=mx;i++)
{
if(s[i]=='1')
{
++n,modify(1,mx,1,n,n,1);
continue;
}
if(s[i]=='2')
{
++n,modify(1,mx,1,n,n,2);
continue;
}
if(s[i]=='U')
{
n--;
continue;
}
if(s[i]=='L')
{
int pos=query_r(1,mx,1,n);
modify(1,mx,1,pos+1,n,2);
modify(1,mx,1,pos,pos,1);
}
if(s[i]=='R')
{
int pos=query_l(1,mx,1,n);
modify(1,mx,1,pos+1,n,1);
modify(1,mx,1,pos,pos,2);
}
}
tree_export(1,mx,1,n,p);
}
int main()
{
//freopen("lx.in","r",stdin);
read(s,sn),read(t,tn);
int sum=abs(sn-tn);
int n=min(sn,tn);
int ans=2*n,dist=0;
for(int i=1;i<=n;i++)
{
if(s[i]==t[i])dist*=2;
else if(s[i]<t[i])dist=dist*2+1;
else
{
dist=dist*2-1;
if(dist<0)swap(s,t),dist*=-1;
}
if(dist>ans)break;
ans=min(ans,dist+2*(n-i));
}
cout<<sum+ans<<endl;
return 0;
}