\(\\\)
Description
- \(n,q \le 10^5,a_i \le 10^9,k \le 2^{30},1 \le l \le r \le n\)
\(\\\)
Solution
先考虑区间与怎么搞。
注意到一个数二进制下至多只有 \(30\) 位,也就是说对这个数有效的与至多只有 \(30\) 次。
于是可以参考区间取模的想法,通过判断区间是否有冲突来决定向下递归。
这样做的复杂度需要按照每一个数至多修改 \(log\) 次算,为 \(O(nlogn)\) 。
怎么判区间与上一个数对当前区间是否有效?可以先到一种巧妙的方式,维护区间与,然后判断是否存在某一位,满足区间与起来的值这一位是 \(1\) ,而修改的数这一位是 \(0\) 。
所以关于修改只需要维护区间或。
区间和部分......
线段树每个节点维护区间和就好了吧。
区间选两个数的和的平方的期望。注意两个数可以选同一位置。
有点意思。注意平方的期望不等于期望的平方。
索性把式子拆开。
\[ E\big((x+y)^2\big)=E\big(x^2+y^2+2xy\big) \]
每一个事件概率相同,考虑从期望的定义出发。
所有事件的答案和就是
\[ \sum_{i=l}^r\sum_{j=l}^r (a_i+a_j)^2=\sum_{i=l}^r\sum_{j=l}^r a_i^2+a_j^2+2a_ia_j=\sum_{i=l}^r\sum_{j=l}^r a_i^2+\sum_{i=l}^r\sum_{j=l}^ra_j^2+\sum_{i=l}^r\sum_{j=l}^r2a_ia_j \]
发现可以再化简,得到
\[ \sum_{i=l}^r\sum_{j=l}^r a_i^2+\sum_{i=l}^r\sum_{j=l}^ra_j^2+\sum_{i=l}^r\sum_{j=l}^r2a_ia_j=2\times(r-l+1)\times\sum_{i=l}^ra_i^2+2\times (\sum_{i=l}^ra_i)^2 \]
注意到二元组的情况数显然是 \((r-l+1)^2\) 而题目答案要乘上这个值,所以不用考虑分母。
所以我们需要维护区间和和区间二次和即可。
\(\\\)
Code
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 100010
#define gc getchar
#define Rg register
#define mid ((l+r)>>1)
#define mod 998244353ll
using namespace std;
typedef long long ll;
inline ll rd(){
ll x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
ll n,m,a[N];
struct segment{
ll root,ptr;
inline ll newnode(){return ++ptr;}
struct node{ll ls,rs,ors,sum1,sum2;}c[N<<1];
inline void pushup(ll rt){
c[rt].ors=c[c[rt].ls].ors|c[c[rt].rs].ors;
c[rt].sum1=(c[c[rt].ls].sum1+c[c[rt].rs].sum1);
c[rt].sum2=(c[c[rt].ls].sum2+c[c[rt].rs].sum2)%mod;
}
void build(ll &rt,ll l,ll r){
rt=newnode();
if(l==r){
c[rt].ors=a[l];
c[rt].sum1=a[l];
c[rt].sum2=a[l]*a[l]%mod;
return;
}
build(c[rt].ls,l,mid);
build(c[rt].rs,mid+1,r);
pushup(rt);
}
void del(ll rt,ll l,ll r,ll x){
if(l==r){
a[l]&=x;
c[rt].ors=a[l];
c[rt].sum1=a[l];
c[rt].sum2=a[l]*a[l]%mod;
return;
}
if((c[c[rt].ls].ors&x)!=c[c[rt].ls].ors) del(c[rt].ls,l,mid,x);
if((c[c[rt].rs].ors&x)!=c[c[rt].rs].ors) del(c[rt].rs,mid+1,r,x);
pushup(rt);
}
void updata(ll rt,ll l,ll r,ll L,ll R,ll x){
if(l>R||r<L) return;
if(l>=L&&r<=R){del(rt,l,r,x);return;}
if(L<=mid) updata(c[rt].ls,l,mid,L,R,x);
if(R>mid) updata(c[rt].rs,mid+1,r,L,R,x);
pushup(rt);
}
ll query1(ll rt,ll l,ll r,ll L,ll R){
if(l>R||r<L) return 0;
if(l>=L&&r<=R) return c[rt].sum1;
ll res=0;
if(L<=mid) res+=query1(c[rt].ls,l,mid,L,R);
if(R>mid) res+=query1(c[rt].rs,mid+1,r,L,R);
return res;
}
ll query2(ll rt,ll l,ll r,ll L,ll R){
if(l>R||r<L) return 0;
if(l>=L&&r<=R) return c[rt].sum2;
ll res=0;
if(L<=mid) res+=query2(c[rt].ls,l,mid,L,R);
if(R>mid) res+=query2(c[rt].rs,mid+1,r,L,R);
return res%mod;
}
}tree;
int main(){
freopen("seg.in","r",stdin);
freopen("seg.out","w",stdout);
n=rd();
for(Rg ll i=1;i<=n;++i) a[i]=rd();
tree.build(tree.root,1,n);
m=rd();
for(Rg ll i=1,op,l,r,x;i<=m;++i){
op=rd();
if(op==1){
l=rd(); r=rd(); x=rd();
tree.updata(tree.root,1,n,l,r,x);
}
else if(op==2){
l=rd(); r=rd();
printf("%lld\n",tree.query1(tree.root,1,n,l,r));
}
else{
l=rd(); r=rd();
ll sum1=tree.query1(tree.root,1,n,l,r)%mod;
ll sum2=tree.query2(tree.root,1,n,l,r);
printf("%lld\n",(2*(r-l+1)*sum2%mod+2*sum1%mod*sum1%mod)%mod);
}
}
return 0;
}