Problem Description
Yuanfang is puzzled with the question below:
There are n integers, a1, a2, …, an. The initial values of them are 0. There are four kinds of operations.
Operation 1: Add c to each number between ax and ay inclusive. In other words, do transformation ak<—ak+c, k = x,x+1,…,y.
Operation 2: Multiply c to each number between ax and ay inclusive. In other words, do transformation ak<—ak×c, k = x,x+1,…,y.
Operation 3: Change the numbers between ax and ay to c, inclusive. In other words, do transformation ak<—c, k = x,x+1,…,y.
Operation 4: Get the sum of p power among the numbers between ax and ay inclusive. In other words, get the result of axp+ax+1p+…+ay p.
Yuanfang has no idea of how to do it. So he wants to ask you to help him.
Input
There are no more than 10 test cases.
For each case, the first line contains two numbers n and m, meaning that there are n integers and m operations. 1 <= n, m <= 100,000.
Each the following m lines contains an operation. Operation 1 to 3 is in this format: “1 x y c” or “2 x y c” or “3 x y c”. Operation 4 is in this format: “4 x y p”. (1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3)
The input ends with 0 0.
Output
For each operation 4, output a single integer in one line representing the result. The answer may be quite large. You just need to calculate the remainder of the answer when divided by 10007.
Sample Input
5 5
3 3 5 7
1 2 4 4
4 1 5 2
2 2 5 8
4 3 5 3
0 0
Sample Output
307
7489
思路
这道题看似是个简单的线段树实则是个扮猪吃老虎的题。先做的落谷3373再来做的这道题要好一点点。难点总共有几处:
-
前三个区间操作怎么进行?按什么顺序进行。首先都能看明白的是更改值的操作更优先,其次就是乘法操作要比加法操作优先,最后更新加法。
-
p = 1,2,3次幂的答案怎么求?s1[k]表示一次方的求和,s2[k]表示二次方的求和,s3[k]表示三次方的求和。
-
对区间[l,r] 整体 + c的求和变形。最后会发现必须先更新s3[k] 再更新 s2[k] 最后更新s1[k]。
对区间[l,r] 整体 + c的求和变形。
s1[k] = (a1 + c) + (a2 + c) + (a3 + c) + .... + (an + c)
= a1 + a2 + a3 + ... + an + (r-l+1) * c
= s1[k] + (r-l+1)*c //这一行的s1[k]是进行当前更新之前的值,下面同理
s2[k] = (a1 + c)^2 + (a2 + c)^2 + (a3 + c)^2 + .... + (an + c)^2
= a1^2 + a2^2 + a3^2 + .... + an^2 + 2a1c + 2a2c + ... + 2anc + (r-l+1) * c^2
= s2[k] + 2*c(a1+a2+a3+...+an) + (r-l+1) * c^2
= s2[k] + 2*c*s1[k] + (r-l+1) * c^2
s3[k] = (a1 + c)^3 + (a2 + c)^3 + ... + (an + c)^3
= a1^3 + 3*c^2*a1 + 3*c*a1^2 + c^3 + .... + an^3 + 3*c^2*an + 3*c*an^2 + c^3
= a1^3 + ... + an^3 + 3c(a1^2 + ... + an^2) + 3c^2(a1 + ... + an) + (r-l+1) * c^3
= s3[k] + 3*c*s2[k] + 3*c^2 *s1[k] + (r-l+1) * c^3
add[k] += c; //加法叠加
- 对区间[l,r] 整体 * c的求和变形。
s1[k] = a1*c + a2*c + a3*c + ... + an*c
= (a1 + a2 + a3 +... + an)*c
= s1[k] * c
s2[k] = (a1*c)^2 + (a2*c)^2 + ... +(an*c)^2
= c^2(a1^2 + a2^2 + ... + an^2)
= c^2*s2[k]
s3[k] = (a1*c)^3 + (a2*c)^3 + ... +(an*c)^3
= c^3(a1^3 + a2^3 + ... + an^3)
= c^3*s3[k]
add[k] = add[k] * c //注意加法操作,比方说先做加法更新在做乘法更新那么根据分配率就可以可知
mul[k] = mul[k] * c //乘法翻倍
- 对区间[l,r] 整体更改值 = c的求和变形,这个操作是最简单的,因为所有的叶节点的值都一样
s1[k] = (r-l+1) * c
s2[k] = (r-l+1) * c^2
s3[k] = (r-l+1) * c^3
mul[k] = 1 //取消乘法标记
add[k] = 0 //取消加法标记
res[k] = c
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
const int p = 10007;
#define lson k<<1,l,m
#define rson k<<1|1,m+1,r
#define ll long long
ll s[3][maxn<<2],add[maxn<<2];
ll res[maxn<<2],mul[maxn<<2];
inline int quickpow(ll a,ll b) //快速幂
{
a = a%p;
int ans = 1;
while(b){
if(b%2 == 1){
ans = (ans * a)%p;
}
b >>= 1;
a = (a * a)%p;
}
return ans;
}
inline void push_up(int k)
{
s[0][k] = s[0][k<<1] + s[0][k<<1|1];
s[1][k] = s[1][k<<1] + s[1][k<<1|1];
s[2][k] = s[2][k<<1] + s[2][k<<1|1];
}
/*
//建不建树无所谓,用memset和fill初始化数组就好了,反正是一个空树
inline void build(int k,int l,int r)
{
mul[k] = 1;
add[k] = res[k] = 0;
if(l == r){
s[0][k] = s[1][k] = s[2][k] = 0;
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
push_up(k);
}
*/
inline void push_down(int k,int l,int r)
{
int m = (l + r) >> 1;
if(res[k]){ //重置
res[k<<1] = res[k<<1|1] = res[k];
add[k<<1] = add[k<<1|1] = 0; //取消左右孩子的标记,自己理解
mul[k<<1] = mul[k<<1|1] = 1; //取消左右孩子的标记
s[0][k<<1] = (ll)(m-l+1)*quickpow(res[k],1)%p;
s[0][k<<1|1] = (ll)(r-m)*quickpow(res[k],1)%p;
s[1][k<<1] = (ll)(m-l+1)*quickpow(res[k],2)%p;
s[1][k<<1|1] = (ll)(r-m)*quickpow(res[k],2)%p;
s[2][k<<1] = (ll)(m-l+1)*quickpow(res[k],3)%p;
s[2][k<<1|1] = (ll)(r-m)*quickpow(res[k],3)%p;
res[k] = 0;
}
if(mul[k] != 1){ //乘法
mul[k<<1] = (ll)mul[k<<1] * quickpow(mul[k],1) % p;
mul[k<<1|1] = (ll)mul[k<<1|1] * quickpow(mul[k],1) % p;
//左右孩子的加法延迟翻倍叠加
add[k<<1] = (ll)(add[k<<1] * quickpow(mul[k],1)) % p;
add[k<<1|1] = (ll)(add[k<<1|1] * quickpow(mul[k],1)) % p;
s[0][k<<1] = (ll)(s[0][k<<1] * quickpow(mul[k],1)) % p;
s[0][k<<1|1] = (ll)(s[0][k<<1|1] * quickpow(mul[k],1)) % p;
s[1][k<<1] = (ll)(s[1][k<<1] * quickpow(mul[k],2)) % p;
s[1][k<<1|1] = (ll)(s[1][k<<1|1] * quickpow(mul[k],2)) % p;
s[2][k<<1] = (ll)(s[2][k<<1] * quickpow(mul[k],3)) % p;
s[2][k<<1|1] = (ll)(s[2][k<<1|1] * quickpow(mul[k],3)) % p;
mul[k] = 1;
}
if(add[k]){ //加法
add[k<<1] = (ll)(add[k<<1] + add[k]) % p;
add[k<<1|1] = (ll)(add[k<<1|1] + add[k]) % p;
s[2][k<<1] = ((ll)s[2][k<<1] + (m-l+1)*quickpow(add[k],3)%p + 3*quickpow(add[k],1)*s[1][k<<1]%p + 3*quickpow(add[k],2)*s[0][k<<1]%p)%p;
s[2][k<<1|1] = ((ll)s[2][k<<1|1] + (r-m)*quickpow(add[k],3)%p + 3*quickpow(add[k],1)*s[1][k<<1|1]%p + 3*quickpow(add[k],2)*s[0][k<<1|1]%p)%p;
s[1][k<<1] = ((ll)s[1][k<<1] + 2*s[0][k<<1]*quickpow(add[k],1)%p + (m-l+1)*quickpow(add[k],2)%p)%p;
s[1][k<<1|1] = ((ll)s[1][k<<1|1] + 2*s[0][k<<1|1]*quickpow(add[k],1)%p + (r-m)*quickpow(add[k],2)%p)%p;
s[0][k<<1] = ((ll)s[0][k<<1] + (m-l+1)*add[k]%p) % p;
s[0][k<<1|1] = ((ll)s[0][k<<1|1] + (r-m)*add[k]%p) % p;
add[k] = 0;
}
}
inline void update(int k,int l,int r,int ql,int qr,int ans,int op)
{
if(qr < l || r < ql){
return ;
}
if(ql <= l && r <= qr){
if(op == 1){ //加法
s[2][k] = ((ll)s[2][k] + (r-l+1)*quickpow(ans,3)%p + 3*quickpow(ans,1)*s[1][k]%p + 3*quickpow(ans,2)*s[0][k]%p)%p;
s[1][k] = ((ll)s[1][k] + 2*s[0][k]*ans%p + (r-l+1)*quickpow(ans,2)%p)%p;
s[0][k] = ((ll)s[0][k] + (r-l+1)*ans%p)%p;
add[k] += ans;
}
if(op == 2){ //乘法
s[0][k] = (s[0][k] * quickpow(ans,1)) % p;
s[1][k] = (s[1][k] * quickpow(ans,2)) % p;
s[2][k] = (s[2][k] * quickpow(ans,3)) % p;
add[k] = add[k] * ans % p;
mul[k] = mul[k] * ans % p;
}
if(op == 3){ //更改
s[0][k] = (ll)(r-l+1)*quickpow(ans,1)%p;
s[1][k] = (ll)(r-l+1)*quickpow(ans,2)%p;
s[2][k] = (ll)(r-l+1)*quickpow(ans,3)%p;
res[k] = ans;
add[k] = 0; //取消加法标记
mul[k] = 1; //取消乘法标记
}
return ;
}
push_down(k,l,r);
int m = (l+r)>>1;
update(lson,ql,qr,ans,op);
update(rson,ql,qr,ans,op);
push_up(k);
}
inline ll query(int k,int l,int r,int ql,int qr,int op)
{
if(qr < l || r < ql){
return 0;
}
if(ql <= l && r <= qr){
return s[op][k];
}
push_down(k,l,r);
int m = (l+r)>>1;
int s1 = query(lson,ql,qr,op)%p;
int s2 = query(rson,ql,qr,op)%p;
return (s1+s2)%p;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
if(n == 0 && m == 0) break;
memset(s,0,sizeof(s));
memset(add,0,sizeof(add));
memset(res,0,sizeof(res));
fill(mul,mul+n+1,1);
// build(1,1,n);
int c,x,y,z;
while(m--){
scanf("%d%d%d%d",&c,&x,&y,&z);
if(c <= 3){
update(1,1,n,x,y,z,c);
}
else{
ll ans = query(1,1,n,x,y,z-1);
printf("%lld\n",ans);
}
}
}
return 0;
}
愿你走出半生,归来仍是少年~