2018.10.22【COGS2633】数列操作e(线段树)

本文介绍了一种使用线段树处理区间加法和区间求和问题的方法,特别是当加法操作涉及到二次函数时。通过将操作转换为二次函数的一般形式,我们能够有效地合并标记并进行区间更新,利用预处理的平方前缀和,可以在O(1)时间内计算出多项式的区间和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


解析:

输出又一次阻挡了我一A的步伐。。。

思路:

线段树维护区间加二次函数,区间求和。

题目中要求的加操作是在第 i i i个位置加上 ( i − l + 1 ) 2 × v (i-l+1)^2\times v (il+1)2×v,显然我们需要展开成二次函数一般形式,这样才能够令它满足加法性质,便于合并标记。

展开后以 i i i为主元就是 i 2 ∗ v + i ∗ ( 2 ∗ v ∗ ( 1 − p o s ) ) + ( p o s − 1 ) 2 ∗ v i^2*v+i*(2*v*(1-pos))+(pos-1)^2*v i2v+i(2v(1pos))+(pos1)2v,显然这三个多项式的合并都可以直接加法,并且我们预处理平方前缀和可以做到 O ( 1 ) O(1) O(1)计算出者三个的区间和,具体实现就在代码里面。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
#define ull unsigned ll

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=100005;

ull sum[N<<2],A[N<<2],B[N<<2],C[N<<2];
ull square[N];

inline void pushnow(int k,int l,int r,cs ull &a,cs ull &b,cs ull &c){
	sum[k]+=(r-l+1)*a+(1ll*r-l+1)*(l+r)/2*b+(square[r]-square[l-1])*c;
	A[k]+=a;B[k]+=b;C[k]+=c;
}

inline void pushdown(int k,int l,int r){
	if(A[k]||B[k]||C[k]){
		int mid=(l+r)>>1;
		pushnow(k<<1,l,mid,A[k],B[k],C[k]);
		pushnow(k<<1|1,mid+1,r,A[k],B[k],C[k]);
		A[k]=B[k]=C[k]=0;
	}
}

inline void pushup(int k){
	sum[k]=sum[k<<1]+sum[k<<1|1];
}

void modify(int k,int l,int r,cs int &ql,cs int &qr,cs ull &pos,cs ull &v){
	if(ql<=l&&r<=qr)return pushnow(k,l,r,(pos-1)*(pos-1)*v,v*2*(1-pos),v);
	pushdown(k,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid)modify(k<<1,l,mid,ql,qr,pos,v);
	if(qr>mid)modify(k<<1|1,mid+1,r,ql,qr,pos,v);
	pushup(k);
}

ull query(int k,int l,int r,cs int &ql,cs int &qr){
	if(ql<=l&&r<=qr)return sum[k];
	int mid=(l+r)>>1;
	pushdown(k,l,r);
	if(ql>mid)return query(k<<1|1,mid+1,r,ql,qr);
	if(qr<=mid)return query(k<<1,l,mid,ql,qr);
	return query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr);
}

ull ans;
int n,m;
signed main(){
	freopen("rneaty.in","r",stdin);
	freopen("rneaty.out","w",stdout);
	n=getint();
	m=getint();
	for(int re i=1;i<=n;++i)square[i]=square[i-1]+(ull)i*i;
	while(m--){
		int op=getint(),l=getint(),r=getint();
		switch(op){
			case 1:{
				ull x=getint();
				modify(1,1,n,l,r,l,x);
				break;
			}
			case 2:{
				ans^=query(1,1,n,l,r);
				break;
			}
		}
	}
	printf("%llu",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值