【线段树】COGS 2633

参考博客,作者: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';
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/83279488