题目描述 Description
给出一个长度为N的序列,初始值全为0。支持三种操作,同时维护区间幂的和。
第一种操作:区间加上一个数。
第二种操作:区间乘上一个数。
第三种操作:区间变成一个数。
输入描述 Input Description
不会超过10组数据,每组数据给出一个N,M<100000。接下来M行描述操作和询问,三种操作分别对应(1,x,y,c),(2,x,y,c),(3,x,y,c),询问对应(4,x,y,p)表示询问从x到y的p次幂的和。(1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3) 一直读入到N=0,M=0为止。
输出描述 Output Description
对于每一次询问输出取模10007后的答案
水题分析 Waterproblem Analysis
首先很轻松的可以看出来这是一道线段树的题目,题目的要求的幂的和,符合线段树的区间可合并性。暂时确定这道题的使用线段树。
接下来看题目要求维护的幂的和,p<=3,我们可以同时维护这三个值然后分别对应的输出即可。但是幂的和与和的幂明显不同不能直接求得,要根据左右儿子的和相加得到。分别用 S 1 S_1 S1, S 2 S_2 S2, S 3 S_3 S3来表示该区间的幂的和。
依次来讨论三个操作。
只考虑当前修改的区间,对于操作一:
S
1
S_1
S1很好求得就是
S
1
S_1
S1+区间长度
L
L
L*加数
X
X
X即。
S 1 ′ = S 1 + L ∗ X S_1'=S_1+L*X S1′=S1+L∗X
求S2时先看区间只有一个数时得情况,设未知数 A A A得
( A + X ) 2 = A 2 + 2 ∗ A ∗ X + X 2 (A+X)^2=A^2+2*A*X+X^2 (A+X)2=A2+2∗A∗X+X2
很容易得到。如果该区间有两个数呢?令这一个数为 B B B易得
( A + X ) 2 + ( B + X ) 2 = ( A 2 + B 2 ) + 2 ∗ X ∗ ( A + B ) + 2 ∗ X 2 (A+X)^2+(B+X)^2=(A^2+B^2)+2*X*(A+B)+2*X^2 (A+X)2+(B+X)2=(A2+B2)+2∗X∗(A+B)+2∗X2
用 S 1 S_1 S1, S 2 S_2 S2用 S 2 ′ S_2' S2′表示来替换可得
S 2 ′ = S 2 + 2 ∗ X ∗ S 1 + L ∗ X 2 S_2'=S_2+2*X*S_1+L*X^2 S2′=S2+2∗X∗S1+L∗X2
可以类推到区间长度为N的情况
求立方和时采用同样的方法可以得到
S 3 ′ = S 3 + 3 ∗ X ∗ S 2 + 3 ∗ S 1 ∗ X 2 + L ∗ X 3 S_3'=S_3+3*X*S_2+3*S_1*X^2+L*X^3 S3′=S3+3∗X∗S2+3∗S1∗X2+L∗X3
到目前为止可以得到操作一的处理方法
{ S 1 ′ = S 1 + L ∗ X S 2 ′ = S 2 + 2 ∗ X ∗ S 1 + L ∗ X 2 S 3 ′ = S 3 + 3 ∗ X ∗ S 2 + 3 ∗ S 1 ∗ X 2 + L ∗ X 3 \left\{ \begin{array}{c} S_1'=S_1+L*X \\ S_2'=S_2+2*X*S_1+L*X^2 \\ S_3'=S_3+3*X*S_2+3*S_1*X^2+L*X^3 \end{array} \right. ⎩⎨⎧S1′=S1+L∗XS2′=S2+2∗X∗S1+L∗X2S3′=S3+3∗X∗S2+3∗S1∗X2+L∗X3
现在来讨论操作二的处理方法:
由 ( K ∗ A ) 2 = K 2 ∗ A 2 (K*A)^2=K^2*A^2 (K∗A)2=K2∗A2, ( K ∗ A ) 3 = K 3 ∗ A 3 (K*A)^3=K^3*A^3 (K∗A)3=K3∗A3得
{ S 1 ′ = K ∗ S 1 S 2 ′ = K 2 ∗ S 2 S 3 ′ = K 3 ∗ S 3 \left\{ \begin{array}{c} S_1'=K*S_1\\ S_2'=K^2*S_2\\ S_3'=K^3*S_3 \end{array} \right. ⎩⎨⎧S1′=K∗S1S2′=K2∗S2S3′=K3∗S3
操作三是最简单:
相当于L个相同的数的平方和,立方和。
{ S 1 ′ = L ∗ X S 2 ′ = L ∗ X 2 S 3 ′ = L ∗ X 3 \left\{ \begin{array}{c} S_1'=L*X\\ S_2'=L*X^2\\ S_3'=L*X^3\\ \end{array} \right. ⎩⎨⎧S1′=L∗XS2′=L∗X2S3′=L∗X3
因为都是区间操作所以我们惯用lazy标记来对子区间进行更新,我们定义三个lazy标记add,mult,to分别表示三种操作。最后一个问题是当一个区间内同时有三个标记时我们如何处理才能保证正确性。
毋庸置疑的是当存在第三种操作时优先执行这一步操作因为,存在这一步操作时已经将在这次操作之前的前两种操作都清零了,所以相对于其他两种操作,第三种操作是最早的,优先处理。同时将左右儿子的lazy更新,前两种操作清零,即
tree[d<<1].lazyto=tree[d<<1|1].lazyto=x;
tree[d<<1].lazymult=tree[d<<1|1].lazymult=1;
tree[d<<1].lazyadd=tree[d<<1|1].lazymult=0;
tree[d].lazyto=0;
前两种操作的先后顺序不好确定。
我们先观察这两种情况
先进行第一种操作再进行第二种操作
(
S
1
+
A
)
∗
B
=
B
∗
S
1
+
A
∗
B
(S_1+A)*B=B*S_1+A*B
(S1+A)∗B=B∗S1+A∗B
先进行第二种操作再进行第一种操作
(
B
∗
S
1
)
+
A
=
B
∗
S
1
+
A
(B*S_1)+A=B*S_1+A
(B∗S1)+A=B∗S1+A
观察可以发现这两种情况我们都可以转换成先乘后加的情况,那么乘法的优先级就比加法高,同时存在时先进行乘法,但传给儿子时要将加数乘上乘数。
tree[d].lazyadd=((tree[d].lazyadd%MOD)*x)%MOD;
tree[d].lazymult=((tree[d].lazymult%MOD)*x)%MOD;
综上所述优先级是
Ⅲ
>
Ⅱ
>
Ⅰ
Ⅲ>Ⅱ>Ⅰ
Ⅲ>Ⅱ>Ⅰ
至此题目结束剩下的就是要细心写就可以了。都是线段树的基本操作。
还要注意模数的操作。同时lazymult要从1开始。
附上代码:
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<set>
#include<stack>
#define ll long long
#define MAXN 500010
#define MAXM 810
#define INF 0x3f3f3f3f
//9223372036854775807
using namespace std;
const int MOD=10007;
int n,q,p=0,tim=0,ans=0;
int fa[MAXN],h[MAXN],in[MAXN],out[MAXN];
int a[810][810];
struct Tree {
int l,r;
ll lazyadd,lazymult,lazyto;
ll sum[4];
}tree[MAXN];
void build(int d,int l,int r) {
tree[d].l=l;tree[d].r=r;
tree[d].lazyadd=tree[d].lazyto=0;
tree[d].lazymult=1;
for(int i=1;i<=3;++i) tree[d].sum[i]=0;
if(l==r)return;
int mid=(tree[d].l+tree[d].r)>>1;
build(d<<1,l,mid);
build(d<<1|1,mid+1,r);
}
void op1(int d,ll x) {
ll temp=x;
ll len=(ll)(tree[d].r-tree[d].l+1)%MOD;
ll a1,a2,a3;
tree[d].sum[3]=((tree[d].sum[3]%MOD)+(((tree[d].sum[2]%MOD)*(temp%MOD)*3)%MOD)+((tree[d].sum[1]*((temp*temp)%MOD)*3)%MOD)+((len*((((temp*temp)%MOD)*temp)%MOD))%MOD))%MOD;
tree[d].sum[2]=((tree[d].sum[2]%MOD)+((((tree[d].sum[1]*temp)%MOD)*2)%MOD)+((len*((temp*temp)%MOD))%MOD))%MOD;
tree[d].sum[1]=((tree[d].sum[1]%MOD)+(len*temp)%MOD)%MOD;//注意顺序,因为要用到改之前的sum所以从3到1
tree[d].lazyadd=((tree[d].lazyadd%MOD)+(x%MOD))%MOD;
}
void op2(int d,ll x) {
tree[d].sum[1]=(tree[d].sum[1]%MOD*x)%MOD;
tree[d].sum[2]=((tree[d].sum[2]%MOD)*((x*x)%MOD))%MOD;
tree[d].sum[3]=((tree[d].sum[3]%MOD)*(((x*x)%MOD)*x)%MOD)%MOD;
tree[d].lazyadd=((tree[d].lazyadd%MOD)*x)%MOD;
tree[d].lazymult=((tree[d].lazymult%MOD)*x)%MOD;
}
void op3(int d,ll x) {
ll len=(tree[d].r-tree[d].l+1)%MOD;
tree[d].sum[1]=(len*x)%MOD;
tree[d].sum[2]=(len*((x*x)%MOD))%MOD;
tree[d].sum[3]=(len*(((x*x)%MOD*x)%MOD))%MOD;
tree[d].lazyto=x;
tree[d].lazymult=1;
tree[d].lazyadd=0;
}
void pushup(int d) {
for(int i=1;i<=3;++i) tree[d].sum[i]=(tree[d<<1].sum[i]+tree[d<<1|1].sum[i])%MOD;
}
void pushdown(int d) {
if(tree[d].lazyto) {
op3(d<<1,tree[d].lazyto);
op3(d<<1|1,tree[d].lazyto);
tree[d].lazyto=0;
}
if(tree[d].lazymult!=1) {
op2(d<<1,tree[d].lazymult);
op2(d<<1|1,tree[d].lazymult);
tree[d].lazymult=1;
}
if(tree[d].lazyadd) {
op1(d<<1,tree[d].lazyadd);
op1(d<<1|1,tree[d].lazyadd);
tree[d].lazyadd=0;
}
}
void modify(int d,int l,int r,ll x,int op) {
if(tree[d].l==l&&tree[d].r==r) {
if(op==1) {
op1(d,x);
}
if(op==2) {
op2(d,x);
}
if(op==3) {
op3(d,x);
}
return;
}
if(tree[d].lazyadd||tree[d].lazymult!=1||tree[d].lazyto) {
pushdown(d);
}
int mid=(tree[d].l+tree[d].r)>>1;
if(r<=mid) {
modify(d<<1,l,r,x,op);
}
else {
if(l>mid) {
modify(d<<1|1,l,r,x,op);
}
else {
modify(d<<1,l,mid,x,op);
modify(d<<1|1,mid+1,r,x,op);
}
}
pushup(d);
}
ll query(int d,int l,int r,int x) {
if(tree[d].l==l&&tree[d].r==r) {
return tree[d].sum[x]%MOD;
}
if(tree[d].lazyadd||tree[d].lazymult!=1||tree[d].lazyto) pushdown(d);
int mid=(tree[d].l+tree[d].r)>>1;
if(r<=mid) {
return query(d<<1,l,r,x)%MOD;
}
else {
if(l>mid) {
return query(d<<1|1,l,r,x)%MOD;
}
else {
return (query(d<<1,l,mid,x)+query(d<<1|1,mid+1,r,x))%MOD;
}
}
}
int main() {
while(scanf("%d%d",&n,&q)!=EOF) {
if(n==0&&q==0)break;
build(1,1,n);
for(int i=1;i<=q;++i) {
int op,l,r;
ll x;
scanf("%d%d%d%lld",&op,&l,&r,&x);
if(op==4) {
printf("%lld\n",query(1,l,r,x));
}
else {
modify(1,l,r,x,op);
}
}
}
}