操作 显然可以用线段树简单维护,平凡。显然比较困难的是操作一。以下所称“操作”代指操作一。

这个操作乍一看就没有什么可维护性,但是你可以发现对区间 操作的时候,除了 这个点,其余点的值都至少减半,这里就有一个很好的性质,因为你多次操作过后肯定会剩下很多个 ,而对 去操作是不会改变其值的,有了这个性质,考虑势能线段树。我们可以考虑暴力操作,维护区间最大值,扫到当前区间最大值的时候如果为 那就直接跳过,考虑如何证明复杂度,显然对于一个点,由于一次对其的修改会使其至少减半,所以对一个点 至多进行 次修改,再加上线段树的复杂度就是

代码:

#include<bits/stdc++.h>
#define ll long long
#define db double
#define vec vector
#define pb push_back
#define pll pair<ll,ll>
#define mkp make_pair
#define il inline
#define endl "\n"
using namespace std;
const ll mod=998244353;
const ll inf=1e18;
ll n,q,a[200005];
#define ls (u<<1)
#define rs (u<<1|1)
struct seg{
    ll l,r,mx,sum;
}w[800005];
void up(ll u){
    w[u].mx=max(w[ls].mx,w[rs].mx);
    w[u].sum=w[ls].sum^w[rs].sum;
}
void bd(ll u,ll l,ll r){
    w[u].l=l,w[u].r=r;
    if(l==r) return w[u].mx=w[u].sum=a[l],void(0);
    ll md=(l+r)>>1;
    bd(ls,l,md),bd(rs,md+1,r);
    up(u);
}
void mdf(ll u,ll l,ll r){
    if(w[u].mx<=0) return;
    if(w[u].l==w[u].r) return w[u].mx=w[u].sum=(w[u].sum/(w[u].l-l+1)),void(0);
    ll md=(w[u].l+w[u].r)>>1;
    if(l<=md) mdf(ls,l,r);
    if(r>md) mdf(rs,l,r);
    up(u);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
    cin>>n>>q;
    for(ll i=1;i<=n;i++) cin>>a[i];
    bd(1,1,n); 
    while(q--){
        ll opt,l,r;
        cin>>opt;
        if(opt==1){
            cin>>l>>r,mdf(1,l,r);
        }else{
            cout<<w[1].sum<<endl;
        }
    }
	return 0;
}