题目背景
SOURCE:NOIP2016-RZZ-2 T3
题目描述
给出这样一棵“二叉树”:
每个节点有左右两个儿子,并如下定义每个节点的高度:假设父亲节点的高度为 h ,那么他的两个儿子的节点的高度都是 h + 1 ,相同高度的所有节点称作一层。
每个节点的左儿子的子树都在右儿子的子树的左边,每一层相邻的两个节点之间有一条边。
下面是一个例子:
每一条图上的路径用一个字符串表示,字符串中的每一个字符表示一个移动。字符仅包含如下五种:
1:表示移动到当前节点的左儿子
2:表示移动到当前节点的右儿子
U:表示移动到当前节点的父亲节点
L:表示移动到当前节点同层的左边的节点(保证当前节点在这一层中不是最左边的节点)
R:表示移动到当前节点同层的右边的节点(保证当前节点在这一层中不是最右边的节点)
用一条路径来表示这条路径的终点,例如路径:221LU 就表示上图中的节点 A 。
给出两条路径,你的任务是求出着两条路径的终点之间的最短路。
输入格式
输入两行,每行一个字符串,分别表示两条路径。
输出格式
输出一行一个整数,表示能得到的串的总数。
样例数据
输入
221LU
12L2
输出
3
备注
【数据规模与约定】
用 D 表示所有经过的节点中,深度最大的节点的深度;S 表示输入字符串的最大长度。
对于 10% 的数据,D≤10。
对于 30% 的数据,D≤50。
对于 50% 的数据,D≤1000。
对于 70% 的数据,D≤20000。
对于 100% 的数据,D≤100000;S≤100000。
分析: 10%:建一棵树,模拟走向,限于下标不能太大,只能过深度<25左右的点。
30%:不建树,直接模拟,记录deep和编号,到左儿子就(编号*2-1),右儿子就(编号*2),向上找父亲就(编号/2)或者(编号/2+1),左右走就是加减1,限于long long,只能过深度<60左右的点。
100%:当确定了两个节点后,最短路就一定是两个节点先分别向上移动到同一高度,然后再沿着这一层中间的边走过去,那么只需要枚举两个节点向上跳到了哪一层即可。
假定题目的输入只有1,2两种字符,那么可以按照层从小到大的顺序枚举,在枚举过程中顺便维护下两个节点在当前这一层的水平距离,这就能用O(n)的时间计算答案。
接下来考虑如何将输入变成只有1,2两种字符。如果我们把向左儿子走看做0,向右儿子走看做1,那么每个节点就对应了一个二进制数,水平移动就对应了加减1,那么可以用一颗线段树来维护进位和退位操作,这样的时间复杂度是O(n*
log2
n)。
代码:
30%(本人考场作,有点小bug,其实是28%)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;ch>='0'&&ch<='9';ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
int lena,lenb;
long long dep[5],bh[5],ans;
char a[100010],b[100010];
int main()
{
freopen("board.in","r",stdin);
freopen("board.out","w",stdout);
bh[1]=1;bh[2]=1;
scanf("%s%s",a+1,b+1);
lena=strlen(a+1);
lenb=strlen(b+1);
for(int i=1;i<=lena;++i)//分别处理两个移动的点
{
if(a[i]=='1')
{
dep[1]++;
bh[1]=bh[1]*2-1;
}
if(a[i]=='2')
{
dep[1]++;
bh[1]=bh[1]*2;
}
if(a[i]=='U')
{
dep[1]--;
if(bh[1]&1)
bh[1]+=1;
bh[1]=bh[1]/2;
}
if(a[i]=='L')
bh[1]--;
if(a[i]=='R')
bh[1]++;
}
for(int i=1;i<=lenb;++i)
{
if(b[i]=='1')
{
dep[2]++;
bh[2]=bh[2]*2-1;
}
if(b[i]=='2')
{
dep[2]++;
bh[2]=bh[2]*2;
}
if(b[i]=='U')
{
dep[2]--;
if(bh[2]&1)
bh[2]+=1;
bh[2]=bh[2]/2;
}
if(b[i]=='L')
bh[2]--;
if(b[i]=='R')
bh[2]++;
}
if(dep[1]>dep[2])
{
while(dep[1]>dep[2])//找最短路,肯定优先移到相同高度
{
dep[1]--;
ans++;
if(bh[1]&1)
bh[1]+=1;
bh[1]=bh[1]/2;
}
while(abs(bh[1]-bh[2])>3)//为什么两个相距大于3就要继续往上移呢?因为两个同时向上移一层,距离+2,而水平距离/2,而到水平距离只有3的时候,向上移一层dis+2,水平距离变成1,就是一样一样的了
{
if(bh[1]&1)
bh[1]+=1;
if(bh[2]&1)
bh[2]+=1;
bh[1]=bh[1]/2;
bh[2]=bh[2]/2;
ans+=2;
}
ans+=abs(bh[1]-bh[2]);
}
else
if(dep[2]>dep[1])
{
while(dep[1]<dep[2])
{
dep[2]--;
ans++;
if(bh[2]&1)
bh[2]+=1;
bh[2]=bh[2]/2;
}
while(abs(bh[1]-bh[2])>3)
{
if(bh[1]&1)
bh[1]+=1;
if(bh[2]&1)
bh[2]+=1;
bh[1]=bh[1]/2;
bh[2]=bh[2]/2;
ans+=2;
}
ans+=abs(bh[1]-bh[2]);
}
else
{
while(abs(bh[1]-bh[2])>3)
{
if(bh[1]&1)
bh[1]+=1;
if(bh[2]&1)
bh[2]+=1;
bh[1]=bh[1]/2;
bh[2]=bh[2]/2;
ans+=2;
}
ans+=abs(bh[1]-bh[2]);
}
cout<<ans<<endl;
return 0;
}
100%(分析那么好理解,实现却这么难==,原谅我不打注释吧orz)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=1e5+5;
struct tree
{
int lazy,sum,len;
}tree[N<<2];
int s[N],t[N],sn,tn;
void build(int l,int r,int now)
{
tree[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)
{
tree[now].lazy=lazy;
tree[now].sum=(lazy-1)*tree[now].len;
}
void pushdown(int now)
{
if(tree[now].lazy)
{
add_lazy(now<<1,tree[now].lazy);
add_lazy(now<<1|1,tree[now].lazy);
tree[now].lazy=0;
}
}
void update(int now)
{
tree[now].sum=tree[now<<1].sum+tree[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)
{
if(x<l)return 0;
if(tree[now].sum==tree[now].len)return 0;
if(l==r)return tree[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)
{
if(x<l)return 0;
if(tree[now].sum==0)return 0;
if(l==r)return tree[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]=tree[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];//static作用是如果你没用到数组后面的,它不会开那些数组,就不会占内存
scanf("%s",s+1);
int lenstep=strlen(s+1);
build(1,lenstep,1);
n=0;
for(int i=1;i<=lenstep;i++)
{
if(s[i]=='1')
{
++n,modify(1,lenstep,1,n,n,1);
continue;
}
if(s[i]=='2')
{
++n,modify(1,lenstep,1,n,n,2);
continue;
}
if(s[i]=='U')
{
n--;
continue;
}
if(s[i]=='L')
{
int pos=query_r(1,lenstep,1,n);
modify(1,lenstep,1,pos+1,n,2);
modify(1,lenstep,1,pos,pos,1);
}
if(s[i]=='R')
{
int pos=query_l(1,lenstep,1,n);
modify(1,lenstep,1,pos+1,n,1);
modify(1,lenstep,1,pos,pos,2);
}
}
tree_export(1,lenstep,1,n,p);
}
int main()
{
freopen("board.in","r",stdin);
freopen("board.out","w",stdout);
read(s,sn),read(t,tn);
int sum=abs(sn-tn);
int n=min(sn,tn);
int ans=2*n,dist=0;
bool flag=1;
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;
}
本题结。