【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=lrj=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);
		}
	}
}
posted @ 2018-11-01 21:09  sadstone  阅读(42)  评论(0编辑  收藏  举报