P3373 【模板】线段树 2
HDU 4578 Transformation
洛谷那道题就是裸的区间乘法,HDU的那道题更复杂,还要求区间幂次的和
先说区间乘法的问题,很自然会想到一个时间戳的问题,因为乘法和加法之间不具有交换律,所以先乘后加和先加后乘结果是不一样的。
那么真的要维护一个时间戳吗,这样显然很不好写。
考虑下方标记时的情况,我们只知道有add标记和mul标记,这时候儿子节点时先乘呢还是先加?
我们在处理标记的时候这样处理,每次有一个mul标记加进来,就把add标记也更新为add*mul,那么之后下放标记就可以先乘后加了,因为所有在乘法之前的add标记都已经被乘过一次了。
P3373 【模板】线段树 2
区间乘法裸题
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int INF = 1<<30;
const int maxn = 1e5+5;
struct node
{
int l,r;
ll sum,mul,add;
}tree[maxn<<2];
int n,m,p;
void push_down(int id)
{
int lson=id<<1,rson=id<<1|1;
tree[lson].sum = (tree[lson].sum*tree[id].mul%p+tree[id].add*(tree[lson].r-tree[lson].l+1)%p)%p;
tree[lson].add = (tree[lson].add*tree[id].mul%p+tree[id].add)%p;
tree[lson].mul = (tree[lson].mul*tree[id].mul)%p;
tree[rson].sum = (tree[rson].sum*tree[id].mul%p+tree[id].add*(tree[rson].r-tree[rson].l+1)%p)%p;
tree[rson].add = (tree[rson].add*tree[id].mul%p+tree[id].add)%p;
tree[rson].mul = (tree[rson].mul*tree[id].mul)%p;
tree[id].mul=1,tree[id].add=0;
}
void push_up(int id)
{
tree[id].sum = (tree[id<<1].sum+tree[id<<1|1].sum)%p;
}
void build(int id,int l,int r)
{
tree[id].l=l,tree[id].r=r;
tree[id].mul = 1,tree[id].add = 0;
if(l==r) {scanf("%d",&tree[id].sum);return ;}
int mid = (l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
push_up(id);
}
void update(int id,int l,int r,int op,int k)
{
if(tree[id].l==l && tree[id].r==r)
{
if(op==2)
{
tree[id].sum = (tree[id].sum+(r-l+1)*k)%p;
tree[id].add = (tree[id].add+k)%p;
}
if(op==1)
{
tree[id].sum = (tree[id].sum*k)%p;
tree[id].add = (tree[id].add*k)%p;
tree[id].mul = (tree[id].mul*k)%p;
}
return ;
}
push_down(id);
int mid = (tree[id].l+tree[id].r)>>1;
if(r<=mid) update(id<<1,l,r,op,k);
else if(l>mid) update(id<<1|1,l,r,op,k);
else
{
update(id<<1,l,mid,op,k);
update(id<<1|1,mid+1,r,op,k);
}
push_up(id);
}
ll query(int id,int l,int r)
{
if(tree[id].l==l && tree[id].r==r)
{
return tree[id].sum%p;
}
push_down(id);
int mid = (tree[id].l+tree[id].r)>>1;
if(r<=mid) return query(id<<1,l,r)%p;
else if(l>mid) return query(id<<1|1,l,r)%p;
else return query(id<<1,l,mid)%p+query(id<<1|1,mid+1,r)%p;
}
int main()
{
int c,l,r,x;
scanf("%d%d%d",&n,&m,&p);
build(1,1,n);
while(m--)
{
scanf("%d",&c);
if(c==3)
{
scanf("%d%d",&l,&r);
printf("%lld\n",query(1,l,r)%p);
}
else
{
scanf("%d%d%d",&l,&r,&x);
update(1,l,r,c,x);
}
}
return 0;
}
HDU 4578 Transformation
这道题把区间加法、区间乘法和区间染色都放到了一起
值得注意的是,显然区间染色优先级最高,只要有染色标记开始打进来,add和mul首先要清空,也就是说add和mul有意义当且仅当他们在染色标记之后出现。
而且需要维护的不止是区间和,还有平方和和立方和
考虑到区间幂次的和,只有1,2,3 因此每一种用一个sum来维护
sum1是很容易得到的
那么sum2和sum3呢?
染色和乘法对于sum2和sum3的更新自然简单
加法的时候可以推一下公式
s
u
m
2
′
=
∑
(
a
i
+
k
)
2
=
∑
a
i
2
+
2
∗
∑
a
i
∗
k
+
(
r
−
l
+
1
)
∗
k
2
sum2' = ∑(ai+k)^{2} = ∑ai^2 + 2*∑ai*k + (r-l+1)*k^2
sum2′=∑(ai+k)2=∑ai2+2∗∑ai∗k+(r−l+1)∗k2
化简之后就是
s
u
m
2
′
=
s
u
m
2
+
2
∗
s
u
m
1
∗
k
+
(
r
−
l
+
1
)
∗
k
2
sum2' = sum2+2*sum1*k+(r-l+1)*k^2
sum2′=sum2+2∗sum1∗k+(r−l+1)∗k2
即新的sum2可以由原来的sum2和sum1更新
s
u
m
3
′
=
∑
(
a
i
+
k
)
3
=
∑
a
i
3
+
3
∗
∑
a
i
2
∗
k
+
3
∗
∑
a
i
∗
k
2
+
(
r
−
l
+
1
)
∗
k
3
sum3' = ∑(ai+k)^3 = ∑ai^3+3*∑ai^2*k+3*∑ai*k^2+(r-l+1)*k^3
sum3′=∑(ai+k)3=∑ai3+3∗∑ai2∗k+3∗∑ai∗k2+(r−l+1)∗k3
化简
s
u
m
3
′
=
s
u
m
3
+
3
∗
s
u
m
2
∗
k
+
3
∗
s
u
m
1
∗
k
2
+
(
r
−
l
+
1
)
∗
k
3
sum3' = sum3+3*sum2*k+3*sum1*k^2+(r-l+1)*k^3
sum3′=sum3+3∗sum2∗k+3∗sum1∗k2+(r−l+1)∗k3
解新的sum3可以由原来的sum3、sum2和sum1更新
要注意的是应该先更新sum3再更新sum2,最后才是sum1
因为要保证更新时用的都是原来的值(可以仔细体会一下)
//挺复杂的一道题
//包括 区间乘法 区间加法 区间染色 和 区间幂次查询
//前三个都好实现
//注意区间乘法和区间加法的混用————如何不考虑顺序
//每次区间乘法时 给加法的lazy也乘上 之后push_down的时候就可以先乘后加
//然后区间幂次的查询就比较技巧了
//由于幂次从1到3可知应该特殊处理三种情况
//即分成 s1,s2,s3三棵树
//然后考虑更新的问题 可以动手算一下发现规律
//具体看代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5+5;
const int mod = 10007;
int s1[maxn<<2],s2[maxn<<2],s3[maxn<<2],add[maxn<<2],mul[maxn<<2],change[maxn<<2];
void push_up(int id)
{
s1[id] = (s1[id<<1]+s1[id<<1|1])%mod;
s2[id] = (s2[id<<1]+s2[id<<1|1])%mod;
s3[id] = (s3[id<<1]+s3[id<<1|1])%mod;
}
void push_down(int id,int l,int r)
{
int mid = (l+r)>>1;
int llen=mid-l+1,rlen=r-mid;
int c1=add[id],c2=mul[id],c3=change[id];
int &l1=s1[id<<1],&l2=s2[id<<1],&l3=s3[id<<1];
int &r1=s1[id<<1|1],&r2=s2[id<<1|1],&r3=s3[id<<1|1];
if(c3)
{
l1 = llen*c3%mod;
l2 = llen*c3%mod*c3%mod;
l3 = llen*c3%mod*c3%mod*c3%mod;
r1 = rlen*c3%mod;
r2 = rlen*c3%mod*c3%mod;
r3 = rlen*c3%mod*c3%mod*c3%mod;
add[id<<1] = add[id<<1|1] = 0;
mul[id<<1] = mul[id<<1|1] = 1;
change[id<<1] = change[id<<1|1] = c3;
}
if(c2>1)
{
l1 = l1*c2%mod;
l2 = l2*c2%mod*c2%mod;
l3 = l3*c2%mod*c2%mod*c2%mod;
r1 = r1*c2%mod;
r2 = r2*c2%mod*c2%mod;
r3 = r3*c2%mod*c2%mod*c2%mod;
add[id<<1] = add[id<<1]*c2%mod;
add[id<<1|1] = add[id<<1|1]*c2%mod;
mul[id<<1] = mul[id<<1]*c2%mod;
mul[id<<1|1] = mul[id<<1|1]*c2%mod;
}
if(c1)
{
l3 = (l3+3*l2%mod*c1%mod+3*l1%mod*c1%mod*c1%mod+llen*c1%mod*c1%mod*c1%mod)%mod;
r3 = (r3+3*r2%mod*c1%mod+3*r1%mod*c1%mod*c1%mod+rlen*c1%mod*c1%mod*c1%mod)%mod;
l2 = (l2+2*l1%mod*c1%mod+llen*c1%mod*c1%mod)%mod;
r2 = (r2+2*r1%mod*c1%mod+rlen*c1%mod*c1%mod)%mod;
l1 = (l1+llen*c1%mod)%mod;
r1 = (r1+rlen*c1%mod)%mod;
add[id<<1] = (add[id<<1]+c1)%mod;
add[id<<1|1] = (add[id<<1|1]+c1)%mod;
}
add[id] = 0;
mul[id] = 1;
change[id] = 0;
}
void build(int id,int l,int r)
{
s1[id] = s2[id] = s3[id]=0;
add[id] = change[id] = 0;
mul[id] = 1;
if(l==r) return ;
int mid = (l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
void update(int id,int stl,int str,int l,int r,int op,int c)
{
if(stl==l && str==r)
{
int len = str-stl+1;
int &t1=s1[id],&t2=s2[id],&t3=s3[id];
if(op==1)
{
t3 = (t3+3*t2%mod*c%mod+3*t1*c%mod*c%mod+c*c%mod*c%mod*len%mod)%mod;
t2 = (t2+2*t1%mod*c%mod+c*c%mod*len%mod)%mod;
t1 = (t1+len*c%mod)%mod;
add[id] = (add[id]+c)%mod;
}
if(op==2)
{
t1 = t1*c%mod;
t2 = t2*c%mod*c%mod;
t3 = t3*c%mod*c%mod*c%mod;
add[id] = add[id]*c%mod;
mul[id] = mul[id]*c%mod;
}
if(op==3)
{
t1 = c*len%mod;
t2 = c*c%mod*len%mod;
t3 = c*c%mod*c%mod*len%mod;
add[id] = 0;
mul[id] = 1;
change[id] = c;
}
return ;
}
push_down(id,stl,str);
int mid = (stl+str)>>1;
if(r<=mid) update(id<<1,stl,mid,l,r,op,c);
else if(l>mid) update(id<<1|1,mid+1,str,l,r,op,c);
else
{
update(id<<1,stl,mid,l,mid,op,c);
update(id<<1|1,mid+1,str,mid+1,r,op,c);
}
push_up(id);
}
int query(int id,int stl,int str,int l,int r,int p)
{
//printf("%d %d %d %d %d %d %d\n",stl,str,l,r,s1[id],s2[id],s3[id]);
if(stl==l && str==r)
{
if(p==1) return s1[id]%mod;
else if (p==2) return s2[id]%mod;
else return s3[id]%mod;
}
push_down(id,stl,str);
int mid = (stl+str)>>1;
if(r<=mid) return query(id<<1,stl,mid,l,r,p)%mod;
else if(l>mid) return query(id<<1|1,mid+1,str,l,r,p)%mod;
else return (query(id<<1,stl,mid,l,mid,p)+query(id<<1|1,mid+1,str,mid+1,r,p))%mod;
}
int main()
{
int n,m;
int op,x,y,c;
while(scanf("%d%d",&n,&m),n||m)
{
build(1,1,n);
while(m--)
{
scanf("%d%d%d%d",&op,&x,&y,&c);
if(op==4) printf("%d\n",query(1,1,n,x,y,c));
else update(1,1,n,x,y,op,c%mod);
}
}
return 0;
}