【COGS 2633】数列操作e

【题目】

传送门

题目描述:

一个长度为 n n n 的序列,一开始序列数的权值都是 0 0 0,有 m m m 次操作

支持两种操作:

  • 1 1 1 l l l r r r x x x,给区间 [ l l l , r r r ] 内,第一个数加 x x x,第二个数加 2 2 ⋅ x 2^2\cdot x 22x,第三个数加 3 2 ⋅ x 3^2⋅x 32x . . . ... ... r − l + 1 r-l+1 rl+1 个数加 ( r − l + 1 ) 2 ⋅ x (r−l+1)^2⋅x (rl+1)2x
  • 2 2 2 l l l r r r 查询区间 [ l l l , r r r ] 内的权值和

每次询问的答案对 2 64 2^{64} 264 取模

输入格式:

第一行两个数 n n n m m m,表示序列长度和操作次数

接下来 m m m 行,每行描述一个操作,有如下两种情况:

  • 1 1 1 l l l r r r x x x,给区间 [ l l l , r r r ] 内,第一个数加 x x x,第二个数加 2 2 ⋅ x 2^2⋅x 22x,第三个数加 3 2 ⋅ x 3^2⋅x 32x…第 r − l + 1 r-l+1 rl+1 个数加 ( r − l + 1 ) 2 ⋅ x (r−l+1)^2⋅x (rl+1)2x
  • 2 2 2 l l l r r r 查询区间 [ l l l , r r r ] 内的权值和

输出格式:

为了减少输出,你只需要输出所有答案对 2 64 2^{64} 264 取膜之后的异或和。

样例数据:

输入
5 5
1 3 4 1
2 1 5
2 2 2
1 3 3 1
1 2 4 1

输出
5

提示:

对于 10 % 10\% 10% 的数据, n , m ≤ 2000 n,m ≤ 2000 n,m2000
对于 30 % 30\% 30% 的数据, n , m ≤ 10000 n,m ≤ 10000 n,m10000
对于 100 % 100\% 100% 的数据, n , m ≤ 100000 n,m ≤ 100000 n,m100000 1 ≤ l ≤ r ≤ n 1 ≤ l ≤ r ≤ n 1lrn 0 ≤ x ≤ 1 0 9 0 ≤ x ≤ 10^9 0x109


【分析】

这道题可以看做是区间加一个二次函数,比较容易可以想到用线段树做

对于一个 1 1 1 l l l r r r x x x 的询问, i i i l ≤ i ≤ r l≤i≤r lir) 位置加上的值为 ( i − l + 1 ) 2 ⋅ x (i-l+1)^2\cdot x (il+1)2x,看起来不太好求区间和,怎么办呢?

实际上把式子拆开,可以得到 i 2 ⋅ x + 2 i ( 1 − l ) ⋅ x + ( l − 1 ) 2 ⋅ x i^2\cdot x+2i(1-l)\cdot x+(l-1)^2\cdot x i2x+2i(1l)x+(l1)2x,那么用三个数组分别维护 x x x 2 ( 1 − l ) ⋅ x 2(1-l)\cdot x 2(1l)x ( l − 1 ) 2 ⋅ x (l-1)^2\cdot x (l1)2x,然后预处理出 i i i i 2 i^2 i2 的前缀和就可以快速合并了,下传也比较容易

然后看到对 2 64 2^{64} 264 取模就直接用 u n s i g n e d unsigned unsigned l o n g long long l o n g long long 就行了


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define ull unsigned long long
using namespace std;
int n,m,L,R;
ull S[N][2],sum[N<<2][3],add[N<<2][3];
void Pushup(int root)
{
	sum[root][0]=sum[root<<1][0]+sum[root<<1|1][0];
	sum[root][1]=sum[root<<1][1]+sum[root<<1|1][1];
	sum[root][2]=sum[root<<1][2]+sum[root<<1|1][2];
}
void Pushnow(int root,int l,int r,ull w1,ull w2,ull w3)
{
	sum[root][0]+=w1*(r-l+1);
	sum[root][1]+=w2*(S[r][0]-S[l-1][0]);
	sum[root][2]+=w3*(S[r][1]-S[l-1][1]);
	add[root][0]+=w1,add[root][1]+=w2,add[root][2]+=w3;
}
void Pushdown(int root,int l,int r,int mid)
{
	Pushnow(root<<1,l,mid,add[root][0],add[root][1],add[root][2]);
	Pushnow(root<<1|1,mid+1,r,add[root][0],add[root][1],add[root][2]);
	add[root][0]=add[root][1]=add[root][2]=0;
}
void Modify(int root,int l,int r,int x,int y,int val)
{
	if(l>=x&&r<=y)
	{
		Pushnow(root,l,r,(ull)val*(L-1)*(L-1),(ull)2*val*(1-L),(ull)val);
		return;
	}
	int mid=(l+r)>>1;
	Pushdown(root,l,r,mid);
	if(x<=mid)  Modify(root<<1,l,mid,x,y,val);
	if(y>mid)  Modify(root<<1|1,mid+1,r,x,y,val);
	Pushup(root);
}
ull Query(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return sum[root][0]+sum[root][1]+sum[root][2];
	ull ans=0;int mid=(l+r)>>1;
	Pushdown(root,l,r,mid);
	if(x<=mid)  ans+=Query(root<<1,l,mid,x,y);
	if(y>mid)  ans+=Query(root<<1|1,mid+1,r,x,y);
	return ans;
}
int main()
{
	freopen("rneaty.in","r",stdin);
	freopen("rneaty.out","w",stdout);
	int s,i,x;ull ans=0;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	{
		S[i][0]=S[i-1][0]+(ull)i;
		S[i][1]=S[i-1][1]+(ull)i*i;
	}
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&s,&L,&R);
		if(s==1)  scanf("%d",&x),Modify(1,1,n,L,R,x);
		if(s==2)  ans^=Query(1,1,n,L,R);
	}
	printf("%llu",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值