hdu-4578-Transformation-線段樹(區間更新區間求和,多lazy,絕世好題)
- Time limit 8000 ms
- Memory limit 65536 kB
Yuanfang is puzzled with the question below:
There are n integers, a 1, a 2, …, a n. The initial values of them are 0. There are four kinds of operations.Operation 1: Add c to each number between a x and a y inclusive. In other words, do transformation a k
Operation 2: Multiply c to each number between a x and a y inclusive. In other words, do transformation a k<---a k×c, k = x,x+1,…,y.
Operation 3: Change the numbers between a x and a y to c, inclusive. In other words, do transformation a k<---c, k = x,x+1,…,y.
Operation 4: Get the sum of p power among the numbers between a x
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
題解:
這道題思路很清晰但寫起來是真的需要功底,需要足夠細緻和對線段樹細節的理解。
首先這道題的本質就是線段樹的區間更新和求和,但是求和時要返回的是區間各元素的和,平方和或立方和。肯定不能真的每次都遍歷到葉子節點然後求個和,那樣百分之二百T。因為只多出來平方和立方兩種值,所以很容易想到我們可以多維護個平方值和立方值。
然後因為有加法(lazy1),乘法(lazy2),賦值(lazy3)三種操作所以我們可以用三個lazy(我這裡直接lazy存操作的c值,有的人用來標記,其實都一樣)。
如果能順理成章的想到以上的思路,那麼這題就完成了三分之一了。
然後需要實現三種運算關於一次方(p1),平方(p2),立方值(p3)操作。
加法:
一次方:區間每個數都加c = 加上 (區間長度len*c)。
平方:因為(a+c)^2 = a^2 + 2ac + c^2 所以區間每個數都加c之後的平方和 = p2 +2*p1*c + len*Mypow(c,2)。
立方:因為(a+c)^3 = a^3 + 3a^2c + 3ac^2 + c^3
所以區間每個數都加c之後的立方和 = p3 + 3*p2*c + 3*p1*Mypow(c,2) + len*Mypow(c,3)。
乘法:
因為(ac)^n = a^n*c^n;
所以一次方:p1*c。
平方:p2*Mypow(c,2)。
立方:p3*Mypow(c,3)。
賦值:
這個沒啥好說的了吧。
一次方:len*c。
平方:len*Mypow(c,2)。
立方:len*Mypow(c,3)。
到這這題算是完成三分之二了。
最後我們要考慮的是如果有兩個甚至三個lazy同時存在咋處理。
因為我們無法通過lazy中存的值看出先後順序,而運算的順序不同結果肯定不同所以我們就需要排除順序的差異。
首先是賦值,如果先進行加法或乘法操作再進行賦值,那麼前面的加法乘法操作肯定是沒有啥意義的
那麼我們可以在給賦值的lazy3賦值的同時清空lazy1和lazy2,這樣的話如果lazy1!=0 或 lazy2>1所代表的加法乘法
運算肯定是在賦值操作之後的。通過這樣我們就可以放心的讓賦值lazy3第一個PushDown了。
同樣的先加後乘和先乘後加的區別在於——(a+b)*c ——(a*c + b)——一個是a*c+b*c 一個是 a*c+b。
差距其實就在最後加的那部分,也就是將lazy1向子區間更新時該加b*c還是b的問題。那麼當我們進行乘操作時可以判斷一下lazy1是否不為0,如果true則代表之前已經有進行加法運算,那麼應該將lazy1乘以c。
到此這道題就完成了。
程式碼:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int MOD = 10007;
struct D{
int p1,p2,p3;//記錄普通,平方,立方的值。
int lazy1,lazy2,lazy3;//分別儲存加法,乘法,賦值運算的c
int len;//記錄區間長度。
}Tree[MAXN*4];
int Mypow(int a,int b){
/*其實這題最多就是立方,所以時間上完全可以不用快速冪
但考慮到pow(10000,3)會超int還是自己寫一個自帶取餘的Mypow吧。*/
int re = 1;
while(b){
if(b%2 != 0){
re *= a;
re %= MOD;
}
a *= a;
a %= MOD;
b >>= 1;
}
return re;
}
void Build(int temp , int left , int right){
Tree[temp].p1 = Tree[temp].p2 = Tree[temp].p3 = 0;
Tree[temp].lazy1 = Tree[temp].lazy3 = 0;
Tree[temp].lazy2 = 1;
Tree[temp].len = right-left+1;
if(left == right)return;
int mid = left + (right-left)/2;
Build(temp<<1,left,mid);
Build(temp<<1|1,mid+1,right);
}
void Up(int temp){
Tree[temp].p1 = ( Tree[temp<<1].p1 + Tree[temp<<1|1].p1 )%MOD;
Tree[temp].p2 = ( Tree[temp<<1].p2 + Tree[temp<<1|1].p2 )%MOD;
Tree[temp].p3 = ( Tree[temp<<1].p3 + Tree[temp<<1|1].p3 )%MOD;
}
void PushDown(int temp){
/*這裡要按先判斷賦值,再判斷乘法,最後判斷加法的順序*/
if(Tree[temp].lazy3){
Tree[temp<<1].lazy3 = Tree[temp<<1|1].lazy3 = Tree[temp].lazy3;
Tree[temp<<1].lazy1 = Tree[temp<<1|1].lazy1 = 0;
Tree[temp<<1].lazy2 = Tree[temp<<1|1].lazy2 = 1;
Tree[temp<<1].p1 = ( (Tree[temp<<1].len) * (Tree[temp].lazy3) )%MOD;
Tree[temp<<1].p2 = ( (Tree[temp<<1].len) * Mypow(Tree[temp].lazy3,2) )%MOD;
Tree[temp<<1].p3 = ( (Tree[temp<<1].len) * Mypow(Tree[temp].lazy3,3) )%MOD;
Tree[temp<<1|1].p1 = ( (Tree[temp<<1|1].len) * (Tree[temp].lazy3) )%MOD;
Tree[temp<<1|1].p2 = ( (Tree[temp<<1|1].len) * Mypow(Tree[temp].lazy3,2) )%MOD;
Tree[temp<<1|1].p3 = ( (Tree[temp<<1|1].len) * Mypow(Tree[temp].lazy3,3) )%MOD;
Tree[temp].lazy3 = 0;
}
if(Tree[temp].lazy2>1){
Tree[temp<<1].lazy2 *= Tree[temp].lazy2;
Tree[temp<<1].lazy2 %= MOD;
if(Tree[temp<<1].lazy1){
Tree[temp<<1].lazy1 *= Tree[temp].lazy2;
Tree[temp<<1].lazy1 %= MOD;
}
Tree[temp<<1|1].lazy2 *= Tree[temp].lazy2;
Tree[temp<<1|1].lazy2 %= MOD;
if(Tree[temp<<1|1].lazy1){
Tree[temp<<1|1].lazy1 *= Tree[temp].lazy2;
Tree[temp<<1|1].lazy1 %= MOD;
}
Tree[temp<<1].p1 *= Tree[temp].lazy2;
Tree[temp<<1].p1 %= MOD;
Tree[temp<<1].p2 *= Mypow(Tree[temp].lazy2,2);
Tree[temp<<1].p2 %= MOD;
Tree[temp<<1].p3 *= Mypow(Tree[temp].lazy2,3);
Tree[temp<<1].p3 %= MOD;
Tree[temp<<1|1].p1 *= Tree[temp].lazy2;
Tree[temp<<1|1].p1 %= MOD;
Tree[temp<<1|1].p2 *= Mypow(Tree[temp].lazy2,2);
Tree[temp<<1|1].p2 %= MOD;
Tree[temp<<1|1].p3 *= Mypow(Tree[temp].lazy2,3);
Tree[temp<<1|1].p3 %= MOD;
Tree[temp].lazy2 = 1;
}
if(Tree[temp].lazy1){
Tree[temp<<1].lazy1 += Tree[temp].lazy1;
Tree[temp<<1].lazy1 %= MOD;
Tree[temp<<1|1].lazy1 += Tree[temp].lazy1;
Tree[temp<<1|1].lazy1 %= MOD;
Tree[temp<<1].p3 = ( (Tree[temp<<1].p3) + (3*Tree[temp<<1].p1*Mypow(Tree[temp].lazy1,2))%MOD + (3*Tree[temp<<1].p2*Tree[temp].lazy1)%MOD + (Tree[temp<<1].len*Mypow(Tree[temp].lazy1,3))%MOD )%MOD;
Tree[temp<<1].p2 = ( (Tree[temp<<1].p2) + (2*Tree[temp<<1].p1*Tree[temp].lazy1)%MOD + (Tree[temp<<1].len*Mypow(Tree[temp].lazy1,2))%MOD )%MOD;
Tree[temp<<1].p1 = ( (Tree[temp<<1].p1) + (Tree[temp<<1].len*Tree[temp].lazy1)%MOD )%MOD;
Tree[temp<<1|1].p3 = ( (Tree[temp<<1|1].p3) + (3*Tree[temp<<1|1].p1*Mypow(Tree[temp].lazy1,2))%MOD + (3*Tree[temp<<1|1].p2*Tree[temp].lazy1)%MOD + (Tree[temp<<1|1].len*Mypow(Tree[temp].lazy1,3))%MOD )%MOD;
Tree[temp<<1|1].p2 = ( (Tree[temp<<1|1].p2) + (2*Tree[temp<<1|1].p1*Tree[temp].lazy1)%MOD + (Tree[temp<<1|1].len*Mypow(Tree[temp].lazy1,2))%MOD )%MOD;
Tree[temp<<1|1].p1 = ( (Tree[temp<<1|1].p1) + (Tree[temp<<1|1].len*Tree[temp].lazy1)%MOD )%MOD;
Tree[temp].lazy1 = 0;
}
}
void Updata(int temp , int left , int right , int ql , int qr , int kind , int value){//kind儲存操作種類,value是值
if(ql>right && qr<left)return ;
if(ql<=left && qr>=right){
//printf("1-------%lld %lld %lld %lld %lld\n",left,right,Tree[temp].p1,Tree[temp].p2,Tree[temp].p3);
if(kind == 3){
Tree[temp].lazy3 = value;
Tree[temp].lazy1 = 0;//要記得把加法的lazy清零
Tree[temp].lazy2 = 1;//乘法lazy賦值1
Tree[temp].p1 = ( (Tree[temp].len) * value )%MOD;
Tree[temp].p2 = ( (Tree[temp].len) * Mypow(value,2) )%MOD;
Tree[temp].p3 = ( (Tree[temp].len) * Mypow(value,3) )%MOD;
}
else if(kind == 2){
Tree[temp].lazy2 *= value;
Tree[temp].lazy2 %= MOD;
if(Tree[temp].lazy1){
//如果此時加法lazy不為0,則代表在乘之前有做加法,則要把加的數乘個value。
Tree[temp].lazy1 *= value;
Tree[temp].lazy1 %= MOD;
}
Tree[temp].p1 *= value;
Tree[temp].p1 %= MOD;
Tree[temp].p2 *= Mypow(value,2);
Tree[temp].p2 %= MOD;
Tree[temp].p3 *= Mypow(value,3);
Tree[temp].p3 %= MOD;
}
else if(kind == 1){
Tree[temp].lazy1 += value;//加法就直接加就好,啥也不用管。
Tree[temp].lazy1 %= MOD;
Tree[temp].p3 = ( (Tree[temp].p3) + (3*Tree[temp].p1*Mypow(value,2))%MOD + (3*Tree[temp].p2*value)%MOD + (Tree[temp].len*Mypow(value,3))%MOD )%MOD;
Tree[temp].p2 = ( (Tree[temp].p2) + (2*Tree[temp].p1*value)%MOD + (Tree[temp].len*Mypow(value,2))%MOD )%MOD;
Tree[temp].p1 = ( (Tree[temp].p1) + (Tree[temp].len*value)%MOD )%MOD;
}
//printf("2-------%lld %lld %lld %lld %lld\n",left,right,Tree[temp].p1,Tree[temp].p2,Tree[temp].p3);
return ;
}
PushDown(temp);
int mid = left + (right-left)/2;
if(ql<=mid)Updata(temp<<1,left,mid,ql,qr,kind,value);
if(qr>mid)Updata(temp<<1|1,mid+1,right,ql,qr,kind,value);
Up(temp);
}
int Query(int temp , int left , int right , int ql , int qr , int pow){//pow儲存返回幾次方的值
if(ql>right || qr<left)return 0;
if(ql<=left && qr>=right){
if(pow == 1)return Tree[temp].p1;
else if(pow == 2)return Tree[temp].p2;
else if(pow == 3)return Tree[temp].p3;
}
PushDown(temp);
int mid = left + (right-left)/2;
int sum = 0;
if(ql<=mid)sum += Query(temp<<1,left,mid,ql,qr,pow);
if(qr>mid)sum += Query(temp<<1|1,mid+1,right,ql,qr,pow);
return sum%MOD;
}
int main(){
int N,M;
int a,b,c,d;
while(scanf("%d %d",&N,&M) && (N || M)){
Build(1,1,N);
while(M--){
scanf("%d %d %d %d",&a,&b,&c,&d);
if(a == 4){
printf("%d\n",Query(1,1,N,b,c,d));
}
else {
Updata(1,1,N,b,c,a,d);
}
}
}
return 0;
}