線段樹 transformation——hdu 4578

小明算法嘎嘎猛發表於2024-09-10

問題描述:
給定一個數列,數列中所有元素都初始化為0,對其執行多種區間操作
操作1:add修改:對區間[L,R]內的所有數加c
操作2:multi修改:對區間[L,R]內所有數乘以c
操作3:change操作:把區間[L,R]內所有數改為c
操作4:sum操作:對區間中的每個數的p次方求和。1<=p<=3
輸入:
有不超過10個測試用例。
對於每個測試用例,第一行包含兩個數字n和m,表示有n個整數和m個操作。其中,1 <= n, m <= 100,000。
接下來的m行,每行包含一個操作。操作1到3的格式為:"1 x y c" 或 "2 x y c" 或 "3 x y c"。操作4的格式為:"4 x y p"。
(其中,1 <= x <= y <= n,1 <= c <= 10,000,1 <= p <= 3)
輸入以0 0結束。
輸出:
對於每個操作4,輸出一個整數作為結果,每個結果佔一行。答案可能非常大,你只需計算答案除以10007的餘數即可。

題目分析:
本題考查對lazy_tag標記的理解,有三種修改操作三種查詢操作,意味著需要三種tag標記,我們分別定義為add[],multi[],change[]標記,需要知道的是他們在記錄變化的時候,存在什麼樣的關係。
(1)做change修改時,原有的add和multi標記失效
(2)做multi修改時,如果原有add,則將add改為addmulti
(3)做線段樹pushdown操作時,先處理change操作,後處理multi,最後執行add
三種查詢操作,求和sum1,平方和sum2,立方和sum3。對於change和multi標記三種查詢都很容易計算,對於add標記sum求和容易計算,但平方和與立方和需要推倒:
平方和sum2:
(a+c)2=a2+c
c+2ac,即sum2[new]=sum2[old]+(R-L+1)cc+2sum1[old]c
立方和
(a+c)3=a3+ccc+3c(a**2+ac),即sum3[new]=sum3[old]+(R-L+1)ccc+3c(sum2[old]+sum1[old]c)
注:公式還需要結合操作二

程式碼:
來自園內大佬

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
using namespace std;
const int MOD = 10007;
const int MAXN = 100010;
struct Node {
	int l, r;
	int sum1, sum2, sum3;
	int lazy1, lazy2, lazy3;
} segTree[MAXN * 3];
void build(int i, int l, int r) {
	segTree[i].l = l;
	segTree[i].r = r;
	segTree[i].sum1 = segTree[i].sum2 = segTree[i].sum3 = 0;
	segTree[i].lazy1 = segTree[i].lazy3 = 0;
	segTree[i].lazy2 = 1;  //乘法標記為1
	int mid = (l + r) / 2;
	if (l == r)return;
	build(i << 1, l, mid);
	build((i << 1) | 1, mid + 1, r);
}
void push_up(int i) {
	if (segTree[i].l == segTree[i].r)
		return;
	segTree[i].sum1 = (segTree[i << 1].sum1 + segTree[(i << 1) | 1].sum1) % MOD;
	segTree[i].sum2 = (segTree[i << 1].sum2 + segTree[(i << 1) | 1].sum2) % MOD;
	segTree[i].sum3 = (segTree[i << 1].sum3 + segTree[(i << 1) | 1].sum3) % MOD;

}

void push_down(int i) {
	if (segTree[i].l == segTree[i].r) return;
	if (segTree[i].lazy3 != 0) {
		segTree[i << 1].lazy3 = segTree[(i << 1) | 1].lazy3 = segTree[i].lazy3;
		//加標記改為0,乘標記改為1即可將標記清除,將原有的標記清除
		segTree[i << 1].lazy1 = segTree[(i << 1) | 1].lazy1 = 0;
		segTree[i << 1].lazy2 = segTree[(i << 1) | 1].lazy2 = 1;
		//左孩子節點更新
		segTree[i << 1].sum1 = (segTree[i << 1].r - segTree[i << 1].l + 1) * segTree[i << 1].lazy3 % MOD;  //c
		segTree[i << 1].sum2 = (segTree[i << 1].r - segTree[i << 1].l + 1) * segTree[i << 1].lazy3 % MOD * segTree[i << 1].lazy3 % MOD;  //c*c
		segTree[i << 1].sum3 = (segTree[i << 1].r - segTree[i << 1].l + 1) * segTree[i << 1].lazy3 % MOD * segTree[i << 1].lazy3 % MOD * segTree[i << 1].lazy3 % MOD; //c*c*c
		//右孩子節點更新
		segTree[(i << 1) | 1].sum1 = (segTree[(i << 1) | 1].r - segTree[(i << 1) | 1].l + 1) * segTree[(i << 1) | 1].lazy3 % MOD;
		segTree[(i << 1) | 1].sum2 = (segTree[(i << 1) | 1].r - segTree[(i << 1) | 1].l + 1) * segTree[(i << 1) | 1].lazy3 % MOD * segTree[(i << 1) | 1].lazy3 % MOD;
		segTree[(i << 1) | 1].sum3 = (segTree[(i << 1) | 1].r - segTree[(i << 1) | 1].l + 1) * segTree[(i << 1) | 1].lazy3 % MOD * segTree[(i << 1) | 1].lazy3 % MOD * segTree[(i << 1) | 1].lazy3 % MOD;
		//標記傳遞後需要刪除
		segTree[i].lazy3 = 0;
	}
	if (segTree[i].lazy1 != 0 || segTree[i].lazy2 != 1) {
		int sum1, sum2, sum3;
		//在做線段樹pushdown操作時,先執行change,再執行multi,最後執行add
		
		//更新標記
		segTree[i << 1].lazy1 = (segTree[i].lazy2 * segTree[i << 1].lazy1 % MOD + segTree[i].lazy1) % MOD;
		segTree[i << 1].lazy2 = segTree[i << 1].lazy2 * segTree[i].lazy2 % MOD;

		//更新sum,在做線段樹pushdown操作時,先執行change,再執行multi,最後執行add
		sum1 = (segTree[i << 1].sum1 * segTree[i].lazy2 % MOD + (segTree[i << 1].r - segTree[i << 1].l + 1) * segTree[i].lazy1 % MOD) % MOD;
		sum2 = (segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[i << 1].sum2 % MOD + 2 * segTree[i].lazy1 * segTree[i].lazy2 % MOD * segTree[i << 1].sum1 % MOD + (segTree[i << 1].r - segTree[i << 1].l + 1) * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD) % MOD;
		sum3 = segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[i << 1].sum3 % MOD;
		sum3 = (sum3 + 3 * segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[i << 1].sum2) % MOD;
		sum3 = (sum3 + 3 * segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD * segTree[i << 1].sum1) % MOD;
		sum3 = (sum3 + (segTree[i << 1].r - segTree[i << 1].l + 1) * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD) % MOD;
		segTree[i << 1].sum1 = sum1;
		segTree[i << 1].sum2 = sum2;
		segTree[i << 1].sum3 = sum3;

		segTree[i << 1 | 1].lazy1 = (segTree[i].lazy2 * segTree[i << 1 | 1].lazy1 % MOD + segTree[i].lazy1) % MOD;
		segTree[i << 1 | 1].lazy2 = segTree[i << 1 | 1].lazy2 * segTree[i].lazy2 % MOD;

		sum1 = (segTree[i << 1 | 1].sum1 * segTree[i].lazy2 % MOD + (segTree[i << 1 | 1].r - segTree[i << 1 | 1].l + 1) * segTree[i].lazy1 % MOD) % MOD;
		sum2 = (segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[i << 1 | 1].sum2 % MOD + 2 * segTree[i].lazy1 * segTree[i].lazy2 % MOD * segTree[i << 1 | 1].sum1 % MOD + (segTree[i << 1 | 1].r - segTree[i << 1 | 1].l + 1) * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD) % MOD;
		sum3 = segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[i << 1 | 1].sum3 % MOD;
		sum3 = (sum3 + 3 * segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[i << 1 | 1].sum2) % MOD;
		sum3 = (sum3 + 3 * segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD * segTree[i << 1 | 1].sum1) % MOD;
		sum3 = (sum3 + (segTree[i << 1 | 1].r - segTree[i << 1 | 1].l + 1) * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD) % MOD;
		segTree[i << 1 | 1].sum1 = sum1;
		segTree[i << 1 | 1].sum2 = sum2;
		segTree[i << 1 | 1].sum3 = sum3;

		//傳遞後需要清除標記
		segTree[i].lazy1 = 0;
		segTree[i].lazy2 = 1;

	}
}
void update(int i, int l, int r, int type, int c) {
	if (segTree[i].l >= l && segTree[i].r <= r) {
		//根據公式填寫即可
		c %= MOD;
		if (type == 1) {
			segTree[i].lazy1 += c;
			segTree[i].lazy1 %= MOD;
			segTree[i].sum3 = (segTree[i].sum3 + 3 * segTree[i].sum2 % MOD * c % MOD + 3 * segTree[i].sum1 % MOD * c % MOD * c % MOD + (segTree[i].r - segTree[i].l + 1) * c % MOD * c % MOD * c % MOD) % MOD;
			segTree[i].sum2 = (segTree[i].sum2 + 2 * segTree[i].sum1 % MOD * c % MOD + (segTree[i].r - segTree[i].l + 1) * c % MOD * c % MOD) % MOD;
			segTree[i].sum1 = (segTree[i].sum1 + (segTree[i].r - segTree[i].l + 1) * c % MOD) % MOD;
		}
		else if (type == 2) {
			segTree[i].lazy1 = segTree[i].lazy1 * c % MOD;
			segTree[i].lazy2 = segTree[i].lazy2 * c % MOD;
			segTree[i].sum1 = segTree[i].sum1 * c % MOD;
			segTree[i].sum2 = segTree[i].sum2 * c % MOD * c % MOD;
			segTree[i].sum3 = segTree[i].sum3 * c % MOD * c % MOD * c % MOD;
		}
		else {
			segTree[i].lazy1 = 0;
			segTree[i].lazy2 = 1;
			segTree[i].lazy3 = c % MOD;
			segTree[i].sum1 = c * (segTree[i].r - segTree[i].l + 1) % MOD;
			segTree[i].sum2 = c * (segTree[i].r - segTree[i].l + 1) % MOD * c % MOD;
			segTree[i].sum3 = c * (segTree[i].r - segTree[i].l + 1) % MOD * c % MOD * c % MOD;
		}
		return;
	}
	push_down(i);
	//二分
	int mid = (segTree[i].l + segTree[i].r) / 2;
	if (l <= mid)update(i << 1, l, r, type, c);
	if (r > mid)update((i << 1) | 1, l, r, type, c);
	push_up(i);
}
int query(int i, int l, int r, int p) {
	if (segTree[i].l >= l && segTree[i].r <= r) {
		if (p == 1)return segTree[i].sum1;
		else if (p == 2)return segTree[i].sum2;
		else return segTree[i].sum3;
	}
	push_down(i);
	int mid = (segTree[i].l + segTree[i].r) / 2;
	int sum = 0;
	if (l <= mid) sum += query(i << 1, l, r, p);
	if (r > mid) sum += query((i << 1) | 1, l, r, p);
	return sum % MOD;
}

int main() {
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int n, m;
	while (scanf("%d%d", &n, &m) == 2) {
		if (n == 0 && m == 0)break;
		build(1, 1, n);
		int type, x, y, c;
		while (m--) {
			scanf("%d%d%d%d", &type, &x, &y, &c);
			if (type == 4)printf("%d\n", query(1, x, y, c));
			else update(1, x, y, type, c);
		}
	}
	return 0;
}

相關文章