给了两种修改:
-
操作1就是区间每个数减去自己的lowbit,由于a[i]是不超过int型范围的,所以操作1最多进行32次,可以单点暴力修改,同时记录这个区间是否都是0,用于剪枝
-
操作2是区间每个数加上highbit,其实也就是最高位的贡献乘2,我们考虑把最高位与其他位分开维护区间和。 这样操作2就乘了区间乘某个数了,经典的线段树维护操作。
-
具体要存的信息就是:
1、二进制下最高位之和 ,涉及区间乘2
2、其余位之和,涉及单点暴力修改
3、区间是否都是0,用于剪枝,如果都是0,那么没必要进行操作1了
4、区间乘2的懒标记
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int mod = 998244353;
ll p2[maxn];
struct node
{
ll sum2;//维护最高位构成的序列的区间和 区间更新是乘积形式
ll sum;//出最高位外的区间和 单点修改
bool have;
int l,r;
ll tag;//区间每个数乘tag
}tree[maxn<<2];
int n;
ll lowbit(ll x)
{
return x&(-x);
}
inline int lc(int rt)
{
return rt<<1;
}
inline int rc(int rt)
{
return rt<<1|1;
}
inline void pushup(int rt)
{
tree[rt].sum2 = (tree[lc(rt)].sum2 + tree[rc(rt)].sum2)%mod;
tree[rt].sum = (tree[lc(rt)].sum + tree[rc(rt)].sum)%mod;
tree[rt].have = tree[lc(rt)].have | tree[rc(rt)].have;
}
inline ll find_h(ll x)
{
for(int i=30;i>=0;i--)
{
if((1ll<<i) <= x) return (1ll<<i);
}
}
inline void build(int rt,int l,int r)
{
tree[rt].tag=1;
tree[rt].l=l,tree[rt].r=r;
if(l == r)
{
int v;
cin>>v;
tree[rt].sum2=find_h(v*1ll);
tree[rt].sum=v-tree[rt].sum2;
tree[rt].have=1