【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】

#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;
}
View Code

 

 

 

 

转载于:https://www.cnblogs.com/CzYoL/p/7215464.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值