P5889 跳树

在这里插入图片描述
在这里插入图片描述
输入:
3 5 4
1 2 3 3 1
1 3 4 5
1 2 2 4
2 3 1
1 1 2 3

输出:
2
1
6

思路: 线段树维护操作结果
首先明确对于平衡二叉树,每个结点编号的二进制代表着它从1走到所处位置的路径其中0代表左儿子,1表示右儿子第一个1是根结点,比如:对于结点5,它的二进制是101,它从1 -> 1的左儿子2 -> 2的右儿子5,分别对应101, 所以可以用数字表示路径,路径之间也有可加性,例如:2+3=5,在2这个结点,只需将从1走到3的路径,复制到2这个结点身上,就可以到5了,相当于将2看作根节点。
维护三个信息:
(1)区间操作中能到达的最高祖先
(2)最深深度
(3)路径

前两个信息维护的原因不赘述,说一下第三个信息,因为可能落在右儿子上,通过前两个信息只能知道终点的深度,不能知道具体在哪棵树上,所以设置个路径变量维护具体位置。
具体操作:假设合并区间a和b,可以发现假设终点深度为d,此时d层有2的d-1次方棵子树,合并的时候,如果终点落在左还是右儿子与区间a无关(即b最高祖先≥a最深深度),最终路径即为区间b的路径;若a区间路径有影响,则将路径通过区间b的改变移动到相应子树某儿子上,详见代码,(看代码就理解了

//几个注意点
//自定义函数里结构体初始化,比如find里的res结构体
//全局里的结构体初始化是0
//位运算一定加上括号,左右儿子还是用2*p,2*p+1写,鬼知道用位运算犯个糊涂要debug多久
//对结构体初始化操作,tree[p] = (ty){0,0,0};(之前课设就可以用

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(x,y) memset(x,y,sizeof(x))
#define all(v) v.begin() , v.end()
#define pb(v) push_back(v)
#define INF 0x3f3f3f3f
#define int long long
#define lson p*2,l,mid
#define rson p*2+1,mid+1,r
typedef long long ll;

const int N = 5e5+10;
int a[N];
int n,m,q;
struct ty
{
    int f;//最高祖先
    int dp;//最深深度
    int num;//路径,每个数字都代表一条路,路径间具有可加性
    ty operator + (const ty &b)const//区间合并直接对+重定义,分两大类,合并后b对num有无影响
    {
        ty ans;
//区间为空的时候可以合并到下面两类
//        if( !f && !dp)
//        {
//            return b;
//        }
//        else if( !b.f && !b.dp )
//        {
//            return  *this;
//        }
        if( dp <= b.f)//num路径被b改变,num对最终路径无贡献
        {
            ans.num = b.num;
            ans.f = f - dp + b.f;
            ans.dp = b.dp;
        }
        else//n最终路径受b的影响叠加
        {
            ans.num = ( (num>>b.f)<<b.dp ) + b.num;
            ans.f = f;
            ans.dp = dp - b.f + b.dp;
        }
        return ans;
    }
}tree[N*4];
void build(int p ,int l, int r )
{
    if( l==r )
    {
        if( a[l] <=2 )
        {
            tree[p].dp = 1;
        }
        if( a[l] == 2)
        {
            tree[p].num = 1;
        }
        if( a[l] == 3)
        {
            tree[p].f=1;
        }
        return ;
    }
    int mid = (l+r)>>1;
    build(lson);
    build(rson);
    tree[p] = tree[2*p] + tree[2*p+1];
    cout<<" p l r f dp num "<<p<<" "<<l<<" "<<r<<" "<<tree[p].f<<" "<<tree[p].dp<<" "<<tree[p].num<<endl;
}
void change(int p, int l, int r ,int x ,int y)
{
    if(l == r)
    {
        tree[p] = (ty){0,0,0};
        if( y <=2 )
        {
            tree[p].dp = 1;
        }
        if( y == 2)
        {
            tree[p].num = 1;
        }
        if( y == 3)
        {
            tree[p].f=1;
        }
        return;
    }
    int mid = (l+r)>>1;
    if( x<= mid ) change(lson,x,y);
    else change(rson,x,y);
    tree[p] = tree[2*p] + tree[2*p+1];

}
ty find(int p ,int l ,int r ,int a, int b)
{
    if( a <= l && r <= b)
    {
        return tree[p];
    }
    ty res=(ty){0,0,0};
    int mid = (l+r)>>1;
    if( a<=mid ) res = find(lson,a,b);
    if( b>=mid+1 ) res = res + find(rson,a,b);
    return res;
}
signed main()
{
    IOS;
    cin>>n>>m>>q;
    _for(i,1,m)
    cin>>a[i];
    build(1,1,m);
    _for(i,1,q)
    {
        int op,l,r,s,x,y;
        ty node;
        cin>>op;
        if( op == 1)
        {
            cin>>s>>l>>r;
            node = find(1,1,m,l,r);//查询操作
            s = (( max((s>>node.f),1ll) )<<node.dp )+node.num;//执行操作
            cout<<s<<endl;
        }
        else
        {
            cin>>x>>y;
            change(1,1,m,x,y);//修改值
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值