NOIP模拟 Board

来源:互联网 发布:录入查询系统源码 编辑:程序博客网 时间:2024/06/06 15:46

给出这样一棵“二叉树”:

每个节点有左右两个儿子,并如下定义每个节点的高度:假设父亲节点的高度为 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+11]这段连续的数,所以可以开两个二进制数组,向坐儿子做末尾加0,向右儿子走末尾加1,但水平移动时涉及进退位,有可能被卡,所以可用一颗线段树来维护二进制数组,这样时间复杂度为O(nlog2n).详见代码。

这里写代码片#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;}
原创粉丝点击