洛谷 P5889 跳树
大体思路很简单,用线段树维护区间操作,询问便是区间查询即可
真正有难度的地方在于操作区间合并,比较难理解。
注意,代码中写的运算符重载只能运用于先进行的操作+后进行的操作
操作区间合并对我这样的蒟蒻还是挺难理解的
sys orz.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=5e5+10;
int n,m,q;
int max(int a,int b){
if(a>b)
return a;
else
return b;
}
int p[maxn];
struct node{
int fm;
int l;
int num;
}tre[maxn*5];
int k;
node operator + (node x,node y){
node ans=(node){0,0,0};
if(!x.fm&&!x.l){
return y;
}
if(!y.fm&&!y.l){
return x;
}
if(x.l>y.fm){
ans.fm=x.fm;
ans.l=x.l-y.fm+y.l;
ans.num=((x.num>>y.fm)<<y.l)+y.num;
}
else{
ans.fm=x.fm+y.fm-x.l;
ans.l=y.l;
ans.num=y.num;
}
return ans;
}
void build(int rt,int l,int r){
if(l==r){
cin>>k;
if(k==1){
tre[rt].l=1;
}
else if(k==2){
tre[rt].l=1;
tre[rt].num=1;
}
else if(k==3){
tre[rt].fm=1;
}
return ;
}
int mid=(l+r)>>1;
build(rt*2,l,mid);
build(rt*2+1,mid+1,r);
tre[rt]=tre[rt*2]+tre[rt*2+1];
return ;
}
node query(int rt,int l,int r,int ql,int qr){
node ans=(node){0,0,0};
if(r<ql||l>qr){
return ans;
}
if(l>=ql&&r<=qr){
return tre[rt];
}
int mid=(l+r)>>1;
if(ql<=mid){
ans=ans+query(rt*2,l,mid,ql,qr);
}
if(qr>mid){
ans=ans+query(rt*2+1,mid+1,r,ql,qr);
}
return ans;
}
void update(int rt,int l,int r,int pos,int x){//??
if(l==r){
tre[rt]=(node){0,0,0};
if(x==1){
tre[rt].l=1;
}
else if(x==2){
tre[rt].l=1;
tre[rt].num=1;
}
else{
tre[rt].fm=1;
}
return;
}
int mid=(l+r)>>1;
if(pos>mid){
update(rt*2+1,mid+1,r,pos,x);
}
else{
update(rt*2,l,mid,pos,x);
}
tre[rt]=tre[rt*2]+tre[rt*2+1];
}
signed main(){
// freopen("a.in","r",stdin);
cin>>n>>m>>q;
//cout<<n<<m<<q;
build(1,1,m);
int t;
node x;
while(q--){
cin>>t;
if(t==1){
int ll,rr,s;
cin>>s>>ll>>rr;
x=query(1,1,m,ll,rr);
// cout<<x.fm<<" "<<x.l<<" "<<x.num<<endl;
cout<<((max(1,s>>x.fm))<<x.l)+x.num<<endl;
}
if(t==2){
int d,y;
cin>>d>>y;
update(1,1,m,d,y);
}
}
return 0;
}