描述
n个数,初始全为0;m次操作,4种,包括:区间加x,区间全部改变为x,区间全乘以x,询问区间内各元素的p次方的总和(1<=p<=3)。
第一种方法
因为p很小,所以维护区间的1次方和s1、2次方和s2、3次方和s3;设定三个lz标记lza、lzm、lzc,表示加、乘、改操作。
分析区间更新操作(区间加要注意更新顺序,len为区间长度)
区间加:
s
3
=
s
3
+
l
e
n
∗
x
3
+
3
∗
x
∗
(
s
2
+
x
∗
s
1
)
s3=s3+len*x^3+3*x*(s2+x*s1)
s3=s3+len∗x3+3∗x∗(s2+x∗s1)
s
2
=
s
2
+
l
e
n
∗
x
2
+
2
∗
x
∗
s
1
s2=s2+len*x^2+2*x*s1
s2=s2+len∗x2+2∗x∗s1
s
1
=
s
1
+
l
e
n
∗
x
s1=s1+len*x
s1=s1+len∗x
区间乘:
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
区间改:
s
1
=
l
e
n
∗
x
s1=len*x
s1=len∗x
s
2
=
l
e
n
∗
x
2
s2=len*x^2
s2=len∗x2
s
3
=
l
e
n
∗
x
3
s3=len*x^3
s3=len∗x3
考虑区间更新时如何设置lz标记
改操作优先级别最高,一旦改后,当前区间的加、乘lz标记清空。
为方便操作,规定先乘后加,即子节点先乘以lzm,再加上lza后可以得到正确值,要维护这一特性,当前区间乘操作时,除了lzm*=x,也要laz*=x。
区间加:
l
z
a
=
l
z
a
+
x
lza = lza+x
lza=lza+x
区间乘:
l
z
m
=
l
z
m
∗
x
lzm = lzm*x
lzm=lzm∗x
l
z
a
=
l
z
a
∗
x
lza = lza*x
lza=lza∗x
区间改:
l
z
c
=
x
lzc = x
lzc=x
l
z
a
=
0
lza = 0
lza=0
l
z
m
=
1
lzm = 1
lzm=1
pushdown操作
先下传改操作,再下传乘操作(注意lzm下传时更新子节点的lza),最后下传加操作。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<vector>
#define cl (k<<1)
#define cr (k<<1|1)
#define Mid ((a[k].l+a[k].r)>>1)
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define bug printf("???\n")
typedef long long LL;
const int Inf=0x3f3f3f3f;
const double eps=1e-7;
const int maxn=1e5+50;
const int mod=10007;
struct N{
int l,r;
LL s1,s2,s3;
LL lza,lzc,lzm;
}a[maxn*4];
void build(int k,int l,int r){
a[k].l = l, a[k].r = r;
a[k].lzm = 1;
a[k].lzc = 0;
a[k].lza = 0;
a[k].s1 = a[k].s2 = a[k].s3 = 0;
if(l==r) return;
build(cl, l, Mid);
build(cr, Mid+1, r);
}
void up(int k){
a[k].s1 = (a[cl].s1 + a[cr].s1)%mod;
a[k].s2 = (a[cl].s2 + a[cr].s2)%mod;
a[k].s3 = (a[cl].s3 + a[cr].s3)%mod;
}
void down(int k){
LL lenl=(a[cl].r-a[cl].l+1)%mod;
LL lenr=(a[cr].r-a[cr].l+1)%mod;
if(a[k].lzc){
a[cl].lzc = a[cr].lzc = a[k].lzc;
a[cl].lzm = a[cr].lzm = 1;
a[cl].lza = a[cr].lza = 0;
LL x=a[k].lzc;
a[cl].s1 = lenl*x %mod;
a[cl].s2 = lenl*x*x %mod;
a[cl].s3 = lenl*x*x*x %mod;
a[cr].s1 = lenr*x %mod;
a[cr].s2 = lenr*x*x %mod;
a[cr].s3 = lenr*x*x*x %mod;
a[k].lzc = 0;
}
if(a[k].lzm!=1){
a[cl].lzm = a[cl].lzm*a[k].lzm%mod;
a[cr].lzm = a[cr].lzm*a[k].lzm%mod;
a[cl].lza = a[cl].lza*a[k].lzm%mod;
a[cr].lza = a[cr].lza*a[k].lzm%mod;
LL x=a[k].lzm;
a[cl].s1 = a[cl].s1*x %mod;
a[cl].s2 = a[cl].s2*x*x %mod;
a[cl].s3 = a[cl].s3*x*x*x %mod;
a[cr].s1 = a[cr].s1*x %mod;
a[cr].s2 = a[cr].s2*x*x %mod;
a[cr].s3 = a[cr].s3*x*x*x %mod;
a[k].lzm = 1;
}
if(a[k].lza){
a[cl].lza = (a[cl].lza + a[k].lza)%mod;
a[cr].lza = (a[cr].lza + a[k].lza)%mod;
LL x=a[k].lza;
a[cl].s3 = (a[cl].s3 + lenl*x*x*x%mod + 3*x*(a[cl].s2 + x*a[cl].s1) )%mod;
a[cl].s2 = (a[cl].s2 + lenl*x*x%mod + 2*x*a[cl].s1)%mod;
a[cl].s1 = (a[cl].s1 + lenl*x)%mod;
a[cr].s3 = (a[cr].s3 + lenr*x*x*x%mod + 3*x*(a[cr].s2 + x*a[cr].s1) )%mod;
a[cr].s2 = (a[cr].s2 + lenr*x*x%mod + 2*x*a[cr].s1)%mod;
a[cr].s1 = (a[cr].s1 + lenr*x)%mod;
a[k].lza = 0;
}
}
void update(int k,int l,int r,LL x, int tp){ //1-add 2-mul 3-change
if(l<=a[k].l&&a[k].r<=r){
LL len=(a[k].r-a[k].l+1)%mod;
if(tp==3){ //change
a[k].lzm = 1;
a[k].lza = 0;
a[k].lzc = x;
a[k].s1 = len*x%mod;
a[k].s2 = len*x*x%mod;
a[k].s3 = len*x*x*x%mod;
}
else
if(tp==2){ //mul
a[k].lza = a[k].lza*x%mod;
a[k].lzm = a[k].lzm*x%mod;
a[k].s1 = a[k].s1*x%mod;
a[k].s2 = a[k].s2*x*x%mod;
a[k].s3 = a[k].s3*x*x*x%mod;
}
else
if(tp==1){ //add
a[k].lza = (a[k].lza+x)%mod;
a[k].s3 = (a[k].s3 + len*x*x*x%mod + 3*x*(a[k].s2 + x*a[k].s1) ) %mod;
a[k].s2 = (a[k].s2 + len*x*x%mod + 2*x*a[k].s1) %mod;
a[k].s1 = (a[k].s1 + len*x) %mod;
}
return;
}
down(k);
if(l<=Mid) update(cl, l, r, x, tp);
if(r> Mid) update(cr, l, r, x, tp);
up(k);
}
LL query(int k,int l,int r,LL p){
if(l<=a[k].l&&a[k].r<=r){
if(p==3) return a[k].s3%mod;
if(p==2) return a[k].s2%mod;
if(p==1) return a[k].s1%mod;
}
down(k);
LL ret=0;
if(l<=Mid) ret = (ret + query(cl, l, r, p))%mod;
if(r> Mid) ret = (ret + query(cr, l, r, p))%mod;
return ret;
}
int main()
{
int n,m;
while(cin>>n>>m, n+m){
build(1, 1, n);
int tp,l,r;
LL x;
for(int i=1; i<=m; i++){
scanf("%d%d%d%lld",&tp,&l,&r,&x);
if(tp<=3) update(1, l, r, x, tp);
else printf("%lld\n",query(1,l,r,x));
}
}
}
第二种方法
此题有一个特殊的前提,初始数据全部为0,且后续操作中有将区间全部改为同一数据,所以可以设置相等标记eq,表示当前区间内值是否一致,每次更新和询问时,只有下传到数据全部相等的区间才操作,区间值相同大大减轻了操作复杂度。面对题目不知所措不妨一试,万一数据水呢?
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<vector>
#define cl (k<<1)
#define cr (k<<1|1)
#define Mid ((a[k].l+a[k].r)>>1)
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define bug printf("???\n")
typedef long long LL;
const int Inf=0x3f3f3f3f;
const double eps=1e-7;
const int maxn=1e5+50;
const int mod=10007;
struct N{
int l,r,v,eq;
}a[maxn*4];
void build(int k,int l,int r){
a[k].l=l; a[k].r=r;
a[k].v=0; a[k].eq=1;
if(l==r) return;
build(cl,l,Mid);
build(cr,Mid+1,r);
}
void down(int k){
a[cl].eq = a[cr].eq = 1;
a[cl].v = a[cr].v = a[k].v;
a[k].eq = 0;
}
void update(int k,int l,int r,int x,int tp){
if(l<=a[k].l&&a[k].r<=r && a[k].eq){
if(tp==1){
a[k].v = (a[k].v+x)%mod;
}
if(tp==2){
a[k].v = (a[k].v*x)%mod;
}
if(tp==3){
a[k].v = x%mod;
}
a[k].eq = 1;
return;
}
if(a[k].eq) down(k);
if(l<=Mid) update(cl,l,r,x,tp);
if(r> Mid) update(cr,l,r,x,tp);
if(a[cl].v==a[cr].v && a[cl].eq && a[cr].eq)
a[k].eq=1, a[k].v=a[cl].v;
else
a[k].eq = 0;
}
int query(int k,int l,int r,int p){
if(l<=a[k].l&&a[k].r<=r && a[k].eq){
int len=(a[k].r-a[k].l+1);
if(p==1){
return a[k].v*len%mod;
}
if(p==2){
return a[k].v*a[k].v%mod*len%mod;
}
if(p==3){
return a[k].v*a[k].v%mod*a[k].v%mod*len%mod;
}
}
if(a[k].eq) down(k);
int ret=0;
if(l<=Mid) ret = (ret + query(cl,l,r,p))%mod;
if(r> Mid) ret = (ret + query(cr,l,r,p))%mod;
return ret;
}
int main()
{
int n,m;
while(cin>>n>>m, n+m){
build(1, 1, n);
int tp,l,r;
int x;
for(int i=1; i<=m; i++){
scanf("%d%d%d%d",&tp,&l,&r,&x);
if(tp<=3) update(1, l, r, x, tp);
else printf("%d\n",query(1,l,r,x));
}
}
}