【题目】
题目描述:
一个长度为 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 22⋅x,第三个数加 3 2 ⋅ x 3^2⋅x 32⋅x . . . ... ...第 r − l + 1 r-l+1 r−l+1 个数加 ( r − l + 1 ) 2 ⋅ x (r−l+1)^2⋅x (r−l+1)2⋅x
- 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 22⋅x,第三个数加 3 2 ⋅ x 3^2⋅x 32⋅x…第 r − l + 1 r-l+1 r−l+1 个数加 ( r − l + 1 ) 2 ⋅ x (r−l+1)^2⋅x (r−l+1)2⋅x
- 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,m≤2000
对于
30
%
30\%
30% 的数据,
n
,
m
≤
10000
n,m ≤ 10000
n,m≤10000
对于
100
%
100\%
100% 的数据,
n
,
m
≤
100000
n,m ≤ 100000
n,m≤100000,
1
≤
l
≤
r
≤
n
1 ≤ l ≤ r ≤ n
1≤l≤r≤n,
0
≤
x
≤
1
0
9
0 ≤ x ≤ 10^9
0≤x≤109
【分析】
这道题可以看做是区间加一个二次函数,比较容易可以想到用线段树做
对于一个 1 1 1 l l l r r r x x x 的询问, i i i( l ≤ i ≤ r l≤i≤r l≤i≤r) 位置加上的值为 ( i − l + 1 ) 2 ⋅ x (i-l+1)^2\cdot x (i−l+1)2⋅x,看起来不太好求区间和,怎么办呢?
实际上把式子拆开,可以得到 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 i2⋅x+2i(1−l)⋅x+(l−1)2⋅x,那么用三个数组分别维护 x x x, 2 ( 1 − l ) ⋅ x 2(1-l)\cdot x 2(1−l)⋅x, ( l − 1 ) 2 ⋅ x (l-1)^2\cdot x (l−1)2⋅x,然后预处理出 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;
}