HDU - 4578 線段樹+三重操作
這道題自己寫了很久,還是沒寫出來,也看了很多題解,感覺多數還是看的迷迷糊糊,最後面看到一篇大佬的才感覺恍然大悟。
先上一篇大佬的題解:https://blog.csdn.net/aqa2037299560/article/details/82872866?tdsourcetag=s_pcqq_aiomsg (既簡單又高效 代碼還短!%%%)
先說下題意:
就是給你n個數,每個數的初始值都是為0
然後給你m個操作
每個操作有 4 個數 op x y c
當 op==1 的時候,把 x到y 範圍內的數 都 加上 c
當 op==2 的時候,把 x到y 範圍內的數 都 乘以
當 op==3 的時候,把 x到y 範圍內的數 都 等於 c
當 op==4 的時候,把 x到y 範圍內的 每一個數 的 c 次方的和 輸出(註意,當op等於4的時候,c的範圍為1~3)
下面說說思路:
關鍵就是在於這個 懶惰值的傳遞,和 區間的每一個值是否都相等的問題
一.先說說樹的數據
這個大家參考下就可以,實現的方法有很多,不一定需要這麽寫,把這個先放上來是便於理解。(我這麽寫是因為我太菜了)
struct Data { ll l, r, val;//分別是左邊界,右邊界,懶惰值(也是該區間每一個葉節點的值)bool dif;//判斷區間內每一個區間是否的相同 }tree[M << 2];
二.再說這個區間值都相等
我們可以知道,如果區間內的每一個值都相等,那麽我們只要 求 其中一個的值的c次方,然後把該數乘以(右邊界 減去 左邊界 再加 1 ),便是該區間值的次方總和了
如果該區間的每一個值不相等,那麽我們必須接著向下探索,直到 找到 一個 區間內的每一個值都相等 的區間,最壞的情況也就是找到葉節點。
三.懶惰值的傳遞
如果這個區間的每一個值都相等,那麽它的左右子區間肯定也都是相等的。
如果這個區間不是全等區間
如果我們在更新數據的過程中,需要用到傳遞懶惰值,那麽肯定是要修改這個區間的某一個子區間,所以傳遞後,這個區間肯定不會再是全等區間
四.數據的更新
我們在更新完值後,肯定也需要更新區間是否相等的信息
有三種情況:
1.如果該區間的左右子區間 都不是 全等區間的話,那這個區間肯定也 不是 全等區間
2.如果該區間的左右子區間 都是 全等區間, 但是它們的 葉節點的值都 不相等,那麽這個區間肯定 也不是 全等區間
3.如果該區間的左右區間 都是 全等區間,並且 它們的 葉節點的值都 全等,那麽這個區間 肯定是 全等區間
下面上代碼:
#include <algorithm> #include <iostream> #include<sstream> #include<iterator> #include<cstring> #include<string> #include<cstdio> #include<cctype> #include<vector> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #define M 100005 #define mod 10007 #define inf 0x3f3f3f3f #define left k<<1 #define right k<<1|1 #define ms(a,b) memset(a,b,sizeof(a)) typedef long long ll; using namespace std; struct Data { ll l, r, val; // 分別是左邊界,右邊界,懶惰值(也是該區間每一個葉節點的值 bool dif;//判斷區間內每一個區間是否的相同 }tree[M << 2]; ll ans, temp; ll op, c, p; int x, y, n, m; void built(int l, int r, int k) { tree[k].l = l, tree[k].r = r, tree[k].val = 0, tree[k].dif = true;//初始化,每一個區間肯定都是全等的 if (l == r)return; int mid = (l + r) >> 1; built(l, mid, left); built(mid + 1, r, right); } void down(int k) { if (tree[k].l == tree[k].r)return;//如果這個數就是子區間那麽沒必要往下傳遞了 tree[k].dif = false;//傳遞後可能不是全等區間了 tree[left].val = tree[right].val = tree[k].val;//傳遞懶惰值 tree[left].dif = tree[right].dif = true;//暫時都是全等區間 } void updata(int k) { if (tree[k].l >= x && tree[k].r <= y && tree[k].dif) {//在要操作的區間範圍內,並且是全等區間就可以直接操作 if (op == 1) {//操作一,加上c tree[k].val = (tree[k].val + c) % mod; } else if (op == 2) {//操縱二,乘以c tree[k].val = (tree[k].val * c) % mod; } else {//操作三,等於c tree[k].val = c % mod; } return; } if (tree[k].dif)down(k);//如果該區間是全等區間,那麽就要傳遞懶惰值,並且傳遞後該區間肯定是 不全等區間 int mid = (tree[k].l + tree[k].r) >> 1; if (x <= mid)updata(left); if (y > mid)updata(right); //傳遞後三種情況分析更新 if (!tree[left].dif || !tree[right].dif)tree[k].dif = false; else { if (tree[left].val != tree[right].val)tree[k].dif = false; else { tree[k].dif = true; tree[k].val = tree[left].val; } } } void query(int k) { if (tree[k].l >= x && tree[k].r <= y && tree[k].dif) {//如果是全等區間,那麽每一個數的值都相等 temp = pow(tree[k].val, c); temp *= (tree[k].r - tree[k].l + 1); ans = (ans + temp) % mod; return; } if (tree[k].dif)down(k);//如果該區間是全等區間,那麽就要傳遞懶惰值,並且傳遞後該區間肯定是 ”全等區間“ int mid = (tree[k].l + tree[k].r) >> 1; if (x <= mid)query(left); if (y > mid)query(right); } int main() { while (scanf("%d%d", &n, &m) != EOF && n && m) { built(1, n, 1); for (int i = 1; i <= m; i++) { scanf("%d%d%d%d", &op, &x, &y, &c); if (op == 4) { ans = 0; query(1); cout << ans << endl; } else { updata(1); } } } return 0; }
HDU - 4578 線段樹+三重操作