[NOIP2017模拟]board

题目背景
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;
}

本题结。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值