原链接
题意简述
给定一个数列,要支持区间加,乘,赋值,和区间求一次,二次,三次方和。
数据
输入
第一行两个正整数 n , m n,m n,m。 n n n表示序列长度, m m m表示操作数。一个操作包含四个整数, o , l , r , x o,l,r,x o,l,r,x, o = 1 , 2 , 3 o=1,2,3 o=1,2,3分别对应在 [ l , r ] [l,r] [l,r]内加/乘/改为 x x x。 o = 4 o=4 o=4表示询问 [ l , r ] [l,r] [l,r]之间的 x x x次方和(如果 o = 4 o=4 o=4,保证 x < = 3 x<=3 x<=3)。多组数据,如果 n = m = 0 n=m=0 n=m=0表示结束。
输出
对于每个 o = 4 o=4 o=4的询问,输出答案。
样例
输入
5 5
3 3 5 7
1 2 4 4
4 1 5 2
2 2 5 8
4 3 5 3
0 0
输出
307
7489
思路
这个题思路很明显,就是用线段树维护一下。就是代码量比较大,思维量也比较大。
首先我们要维护三个
l
a
z
y
t
a
g
lazytag
lazytag,三个值,三个
l
a
z
y
t
a
g
lazytag
lazytag分别命名为
a
(
即
a
d
d
i
t
i
o
n
)
,
m
(
即
m
u
l
i
p
l
i
c
a
t
i
o
n
)
,
c
(
即
c
h
a
n
g
e
)
a(即addition),m(即muliplication),c(即change)
a(即addition),m(即muliplication),c(即change),分别表示加标记,乘标记,赋值标记。
三个值分别为
s
1
,
s
2
,
s
3
s1,s2,s3
s1,s2,s3,表示区间的一次方和,二次方和,三次方和。
当我们对区间进行赋值操作的时候,显然这优先级非常高,会覆盖掉所有的其他标记。此时设我们赋的值是 x x x,长度为 n n n,那么 s 1 = x ∗ n s1=x*n s1=x∗n, s 2 = x 2 ∗ n s2=x^2*n s2=x2∗n, s 3 = x 3 ∗ n s3=x^3*n s3=x3∗n.
当我们对区间进行乘法操作的时候,会对加法标记产生影响。设我们乘了 x x x,原来加标记是 a a a,表示我们加过 a a a,那么现在就要加 a x ax ax。同时,我们的 s 1 = s 1 ∗ x s1=s1*x s1=s1∗x, s 2 = s 2 ∗ x 2 s2=s2*x^2 s2=s2∗x2, s 3 = s 3 ∗ x 3 s3=s3*x^3 s3=s3∗x3。这十分显然。
最难的就是加法了。请系好安全带,前方高能(道路千万条,安全第一条)。设我们的区间是
l
l
l到
r
r
r的,那么我们
+
x
+x
+x的时候,显然
s
1
=
s
1
+
x
∗
n
s1=s1+x*n
s1=s1+x∗n。那么
s
2
s_2
s2呢?
设原数组是
s
s
s,那么
s
2
=
∑
i
=
l
r
(
s
i
+
x
)
由
s
2
的
定
义
得
=
∑
i
=
l
r
(
s
i
2
+
2
s
i
x
+
x
2
)
初
中
学
的
平
方
和
公
式
(
不
会
去
找
初
中
老
师
)
=
(
∑
i
=
l
r
s
i
2
)
+
(
2
x
∑
i
=
l
r
s
i
)
+
(
x
2
∗
n
)
拆
开
了
加
号
,
并
提
了
点
东
西
出
来
=
s
2
+
2
x
∗
s
1
+
x
2
∗
n
由
定
义
得
s_2=\sum\limits_{i=l}^{r}(s_i+x)\\ 由s2的定义得\\ =\sum\limits_{i=l}^{r}(s_i^2+2s_ix+x^2)\\ 初中学的平方和公式(不会去找初中老师)\\ =(\sum\limits_{i=l}^{r}s_i^2)+(2x\sum\limits_{i=l}^{r}s_i)+(x^2*n)\\ 拆开了加号,并提了点东西出来\\ =s_2+2x*s_1+x^2*n\\ 由定义得
s2=i=l∑r(si+x)由s2的定义得=i=l∑r(si2+2six+x2)初中学的平方和公式(不会去找初中老师)=(i=l∑rsi2)+(2xi=l∑rsi)+(x2∗n)拆开了加号,并提了点东西出来=s2+2x∗s1+x2∗n由定义得
接下来求
s
3
s_3
s3。
s
3
=
∑
i
=
l
r
(
s
i
+
x
)
3
由
定
义
=
∑
i
=
l
r
(
s
i
3
+
3
s
i
2
x
+
3
s
i
x
2
+
x
3
)
初
中
学
的
立
方
和
公
式
=
(
∑
i
=
l
r
s
i
3
)
+
(
3
x
∑
i
=
l
r
s
i
2
)
+
(
3
x
2
∑
i
=
l
r
s
i
)
+
x
3
∗
n
拆
开
了
加
号
,
并
提
了
点
东
西
出
来
=
s
3
+
3
x
s
2
+
3
x
2
s
1
+
x
3
∗
n
s3=\sum\limits_{i=l}^{r}(s_i+x)^3 \\ 由定义 \\ =\sum\limits_{i=l}^{r}(s_i^3+3s_i^2x+3s_ix^2+x^3)\\ 初中学的立方和公式\\ =(\sum\limits_{i=l}^{r}s_i^3)+(3x\sum\limits_{i=l}^{r}s_i^2)+(3x^2\sum\limits_{i=l}^{r}s_i)+x^3*n 拆开了加号,并提了点东西出来\\ =s_3+3xs_2+3x^2s_1+x^3*n
s3=i=l∑r(si+x)3由定义=i=l∑r(si3+3si2x+3six2+x3)初中学的立方和公式=(i=l∑rsi3)+(3xi=l∑rsi2)+(3x2i=l∑rsi)+x3∗n拆开了加号,并提了点东西出来=s3+3xs2+3x2s1+x3∗n
到此,我们已经推出公式了。那么,到底谁先更新呢?
我们发现,
s
3
s_3
s3的式子中涉及到所有的
s
1
,
2
,
3
s_{1,2,3}
s1,2,3,所以肯定要先更新,要不然等别的更好了,就会出现错误的值。同理,其次是
s
2
s_2
s2,最后是
s
1
s_1
s1。
然后求和的时候,判一下即可。代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
//Flandle_Scarlet is my wife
{
#define N 100100
#define mod 10007
#define int long long
class SegmentTree
{
private:
struct node
{
int l,r;
//区间左,右
int s1,s2,s3;
//一次,二次,三次的和
int a,m,c;
//加(add),乘(multiply),改(change)的标记
//c=-1表示未进行过修改
}tree[N<<2];
public:
#define ls index<<1
#define rs index<<1|1
#define L tree[index].l
#define R tree[index].r
#define S1 tree[index].s1
#define S2 tree[index].s2
#define S3 tree[index].s3
#define A tree[index].a
#define M tree[index].m
#define C tree[index].c
#define lL tree[ls].l
#define lR tree[ls].r
#define lS1 tree[ls].s1
#define lS2 tree[ls].s2
#define lS3 tree[ls].s3
#define lA tree[ls].a
#define lM tree[ls].m
#define lC tree[ls].c
#define rL tree[rs].l
#define rR tree[rs].r
#define rS1 tree[rs].s1
#define rS2 tree[rs].s2
#define rS3 tree[rs].s3
#define rA tree[rs].a
#define rM tree[rs].m
#define rC tree[rs].c
//奇怪的define(还是能看懂的吧。。。)
void Update(int index)
{
S1=(lS1+rS1)%mod;
S2=(lS2+rS2)%mod;
S3=(lS3+rS3)%mod;
}//和推上来
void Build(int l,int r,int index)//建树
{
L=l,R=r;
A=S1=S2=S3=0;
M=1,C=-1;//设置初始值
//注意M的初始值是1!!!
if (l==r) return;
int mid=(l+r)>>1;
Build(l,mid,ls);
Build(mid+1,r,rs);
Update(index);
}
void Add1(int x,int index)
{
//上文中说的n在这里写成R-L+1的形式,表示区间长度
A=(A+x)%mod;
//A+=x
S3=(S3+3*x*S2%mod+3*x*x%mod*S1%mod+(R-L+1)*x%mod*x%mod*x%mod)%mod;
//S3=S3+3*x*S2+3*x*x*S1+n*x*x*x
S2=(S2+2*x*S1%mod+(R-L+1)*x%mod*x%mod)%mod;
//S2=S2+2*x*S1+n*x*x
S1=(S1+x*(R-L+1))%mod;
//S1=S1+x*n
}
void Mul1(int x,int index)
{
M=M*x%mod;
A=A*x%mod;
S3=S3*x%mod*x%mod*x%mod;
//S3=S3*x^3
S2=S2*x%mod*x%mod;
//S2=S2*x^2
S1=S1*x%mod;
//S1=S1*x
}
void Change1(int x,int index)
{
C=x%mod;
M=1,A=0;
//有了赋值标记就珂以忽略剩下的所有标记了
S3=(x*x%mod*x%mod)%mod*(R-L+1)%mod;
S2=(x*x%mod)%mod*(R-L+1)%mod;
S1=x*(R-L+1)%mod;
}
void PushDown(int index)
{
if (~C)
{
Change1(C,ls);
Change1(C,rs);
C=-1;
}
if (M!=1)
{
Mul1(M,ls);
Mul1(M,rs);
M=1;
}
if (A)
{
Add1(A,ls);
Add1(A,rs);
A=0;
}
}//注意优先级
void Add(int l,int r,int x,int index)
{
if (l>R or L>r) return;
if (l<=L and R<=r)
{
Add1(x,index);return;
}
PushDown(index);//千万不要忘了PushDown!
Add(l,r,x,ls);
Add(l,r,x,rs);
Update(index);//非常明显的写法。。。
}
void Mul(int l,int r,int x,int index)
{
if (l>R or L>r) return;
if (l<=L and R<=r)
{
Mul1(x,index);return;
}
PushDown(index);//千万不要忘了PushDown!
Mul(l,r,x,ls);
Mul(l,r,x,rs);
Update(index);//结构很清晰吧。。。
}
void Change(int l,int r,int x,int index)
{
if (l>R or L>r) return;
if (l<=L and R<=r)
{
Change1(x,index);return;
}
PushDown(index);//千万不要忘了PushDown!
Change(l,r,x,ls);
Change(l,r,x,rs);
Update(index);//不想解释。。。
}
int Query(int l,int r,int p,int index)
{
if (l>R or L>r) return 0;
if (l<=L and R<=r)
{
if (p==1) return S1%mod;
else if (p==2) return S2%mod;
else return S3%mod;
}
PushDown(index);//尤其是这里不要忘了PushDown!(我这里忘了WA了三次)
return (Query(l,r,p,ls)+Query(l,r,p,rs))%mod;
//基本的左右递归。。。
}
}T;
void Query()
{
int n,m;
while(1)
{
scanf("%lld%lld",&n,&m);
if (n==0 and m==0) break;
T.Build(1,n,1);
for(int i=1;i<=m;++i)
{
int o,l,r,x;
scanf("%lld%lld%lld%lld",&o,&l,&r,&x);
if (o==1)
{
T.Add(l,r,x,1);
}
if (o==2)
{
T.Mul(l,r,x,1);
}
if (o==3)
{
T.Change(l,r,x,1);
}
if (o==4)
{
printf("%lld\n",T.Query(l,r,x,1)%mod);
}
}
}
}
void Main()
{
if (0)
{
freopen("in.txt","r",stdin);
freopen("ans.txt","w",stdout);
}
Query();
}
#undef int//long long
#undef N//100100
#undef mod//10007
};
int main()
{
Flandle_Scarlet::Main();
return 0;
}