【JZOJ5943】树
Description
有一个长度为n的序列,第 i i i个数为 a i a_i ai,需要支持区间按位与运算上k,查询和与查询 ∑ i = l r ∑ j = l r a i a j \sum_{i=l}^r\sum_{j=l}^ra_ia_j ∑i=lr∑j=lraiaj。
Solution
注意到每个数只会被修改有限次,线段树上对一个区间记录 a i a_i ai的按位或的和,按位与的时候可以暴力修改有影响的区间。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
typedef long long ll;
const int N=1e5+10,mo=998244353;
int tr[N<<2];
ll s[N<<2],s2[N<<2];
ll a[N];
int n;
void update(int v){
tr[v]=tr[v<<1]|tr[v<<1|1];
s[v]=s[v<<1]+s[v<<1|1];
s2[v]=(s2[v<<1]+s2[v<<1|1])%mo;
}
void modify(int x,int y,int t,int v=1,int l=1,int r=n){
if(l>=x && r<=y){
if((tr[v]&t)==tr[v]) return;
tr[v]=tr[v]&t;
}
if(l==r){
s[v]=tr[v];
s2[v]=s[v]%mo*s[v]%mo;
return;
}
int mid=(l+r)>>1;
if(y<=mid) modify(x,y,t,v<<1,l,mid);
else if(x>mid) modify(x,y,t,v<<1|1,mid+1,r);
else modify(x,mid,t,v<<1,l,mid),modify(mid+1,y,t,v<<1|1,mid+1,r);
update(v);
}
void build(int v=1,int l=1,int r=n){
if(l==r){
tr[v]=a[l];
s[v]=a[l];
s2[v]=s[v]*s[v]%mo;
return;
}
int mid=(l+r)>>1;
build(v<<1,l,mid),build(v<<1|1,mid+1,r);
update(v);
}
ll an1=0,an2=0;
void sum(int x,int y,int v=1,int l=1,int r=n){
if(l==x && r==y){
an1+=s[v],an2=(an2+s2[v])%mo;
return;
}
int mid=(l+r)>>1;
if(y<=mid) sum(x,y,v<<1,l,mid);
else if(x>mid) sum(x,y,v<<1|1,mid+1,r);
else sum(x,mid,v<<1,l,mid),sum(mid+1,y,v<<1|1,mid+1,r);
}
int main()
{
freopen("seg.in","r",stdin);
freopen("seg.out","w",stdout);
scanf("%d",&n);
fo(i,1,n) scanf("%lld",&a[i]);
build();
int q;
scanf("%d",&q);
while(q--){
int op,l,r;
scanf("%d %d %d",&op,&l,&r);
if(op==1){
int x;
scanf("%d",&x);
modify(l,r,x);
}
else{
an1=an2=0;
sum(l,r);
if(op==2){
printf("%lld\n",an1);
continue;
}
an1%=mo;
ll ans=((an1*an1+(r-l-1)*an2)%mo*2%mo+an2*4%mo)%mo;
printf("%lld\n",ans);
}
}
}