1. 程式人生 > >HDU - 4578 線段樹+三重操作

HDU - 4578 線段樹+三重操作

它的 邊界 pac 但是 val ans string ace scanf

這道題自己寫了很久,還是沒寫出來,也看了很多題解,感覺多數還是看的迷迷糊糊,最後面看到一篇大佬的才感覺恍然大悟。

先上一篇大佬的題解: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 範圍內的數 都 乘以

c

  當 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 線段樹+三重操作