参考博客,作者:dreaming__ldx,来源:CSDN
【题目大意】
一个长度为n的序列,一开始序列数的权值都是0,有m次操作
支持两种操作,
1 L R x,给区间[L,R]内,第一个数加x,第二个数加2^2⋅x,第三个数加3^2⋅x...第R-L+1个数加(R−L+1)^2⋅x
2 L R 查询区间[L,R]内的权值和
每次询问的答案对2^64取模
【输入格式】
第一行两个数n,m,表示序列长度和操作次数
接下来m行,每行描述一个操作,有如下两种情况:
1 L R x,给区间[L,R]内,第一个数加x,第二个数加2^2⋅x,第三个数加3^2⋅x...第R-L+1个数加(R−L+1)^2⋅x
2 L R 查询区间[L,R]内的权值和
【输出格式】
为了减少输出,你只需要输出所有答案对2^64取膜之后的异或和。
【样例输入】
5 5
1 3 4 1
2 1 5
2 2 2
1 3 3 1
1 2 4 1
【样例输出】
5
【提示】
对于10%的数据 n,m<=2000
对于30%的数据 n,m<=10000
对于100%的数据,n,m<=100000,1<=L<=R<=n,0<=x<=1e9
【思路】这里相当于是区间加一个二次函数。对于一个L到R的修改,第i项相当于是加了((i-(L-1))^2)*x。
这里用线段树不好直接维护。考虑把变量分离一下。
如果我们把下标i作为主元,那么这个函数就可以拆成 i^2 *x + i*(2-2*L) *x + (L-1)^2 *x。
那么可以把这个函数分成三个部分分开维护。
三个标记分别记录i^2、i和常数项的系数就好了。
然后三坨就用三个区间和分开来维护。
对于i^2和i,用一个数组预处理一下就行了。
下传标记的时候乘一乘就好了。unsigned long long 会自动溢出,就是自动对2^64取模。预处理的时候循环里的i也是ull类型!
#include<bits/stdc++.h>
#define lc (root<<1)
#define rc (root<<1|1)
#define mid (T[root].l+T[root].r>>1)
#define ull unsigned long long
using namespace std;
const ull maxn=1e5+5;
ull n,m,L,R,op,x;
ull cal[maxn][2],ans=0;
struct node{
ull l,r;
ull add[3],sum[3];
}T[maxn<<2];
void read(ull &x){
x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
void build(ull root,ull l,ull r){
T[root].l=l,T[root].r=r;
if(l==r) return;
build(lc,l,mid),build(rc,mid+1,r);
}
inline void pushup(ull root){
for(ull i=0;i<3;++i)
T[root].sum[i]=T[lc].sum[i]+T[rc].sum[i];
}
inline void pushnow(ull root,ull w1,ull w2,ull w3){
T[root].sum[0]+=(T[root].r-T[root].l+1)*w1;
T[root].sum[1]+=(cal[T[root].r][0]-cal[T[root].l-1][0])*w2;
T[root].sum[2]+=(cal[T[root].r][1]-cal[T[root].l-1][1])*w3;
T[root].add[0]+=w1;
T[root].add[1]+=w2;
T[root].add[2]+=w3;
}
inline void pushdown(ull root){
pushnow(lc,T[root].add[0],T[root].add[1],T[root].add[2]);
pushnow(rc,T[root].add[0],T[root].add[1],T[root].add[2]);
T[root].add[0]=T[root].add[1]=T[root].add[2]=0;
}
void update(ull root,ull L,ull R,ull x,ull pos){
if(L<=T[root].l&&R>=T[root].r){
pushnow(root,(ull)(pos-1)*(pos-1)*x,(ull)2*(1-pos)*x,(ull)x);
return;
}
pushdown(root);
if(L<=mid) update(lc,L,R,x,pos);
if(R>mid) update(rc,L,R,x,pos);
pushup(root);
}
ull query(ull root,ull L,ull R){
if(L<=T[root].l&&R>=T[root].r)
return T[root].sum[0]+T[root].sum[1]+T[root].sum[2];
pushdown(root);
ull ret=0;
if(L<=mid) ret+=query(lc,L,R);
if(R>mid) ret+=query(rc,L,R);
return ret;
}
signed main(){
freopen("rneaty.in","r",stdin);
freopen("rneaty.out","w",stdout);
read(n),read(m);
build(1,1,n);
for(ull i=1;i<=n;++i){
cal[i][0]=cal[i-1][0]+i;
cal[i][1]=cal[i-1][1]+i*i;
}
while(m--){
read(op),read(L),read(R);
if(op==1){
read(x);
update(1,L,R,(ull)x,(ull)L);
}
if(op==2) ans^=query(1,L,R);
}
cout<<ans<<'\n';
}