1. 程式人生 > >【線段樹】COGS 2633

【線段樹】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';
}