1. 程式人生 > 實用技巧 >線段樹求解連續區間問題

線段樹求解連續區間問題

lsum 、lmax 、rsum 、 rmax 都是代表著從最左邊開始或者從最右邊開始

第一類:

僅僅要求求解的區間要求是連續的

我們考慮設定三個變數 lsum , rsum , sum 分別記錄從左邊開始的連續區間大小,從右邊開始的連續區間的大小,整個區間的連續區間的大小的最大值

然後我們採取線段樹去維護這三個變數就可以了

因為我們要求的是連續的區間,所以我們 pushup 操作的時候也需要確保我們的區間是連續的

sum 維護的是整個範圍內最大的連續區間 : 左右兩個區間最大的或者是在中間的某一段

這一類連續區間的求解問題的典型例題:

P2894 [USACO08FEB]Hotel G

題目連結:https://www.luogu.com.cn/problem/P2894

#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <cmath>
#include <cstdio>
#include <iomanip>
#include 
<ctime> #include <bitset> #include <cmath> #include <sstream> #include <iostream> #include <unordered_map> #define ll long long #define ull unsigned long long #define ls nod<<1 #define rs (nod<<1)+1 #define pii pair<int,int> #define mp make_pair #define
pb push_back #define INF 0x3f3f3f3f3f3f3f3f #define max(a, b) (a>b?a:b) #define min(a, b) (a<b?a:b) const double eps = 1e-10; const int maxn = 5e4 + 10; const ll MOD = 99999999999999; int sgn(double a) { return a < -eps ? -1 : a < eps ? 0 : 1; } using namespace std; int sum[maxn << 2],lazy[maxn << 2]; int lsum[maxn << 2],rsum[maxn << 2]; void build(int l,int r,int nod) { sum[nod] = lsum[nod] = rsum[nod] = (r-l+1); lazy[nod] = -1; if (l == r) return ; int mid = (l + r) >> 1; build(l,mid,ls); build(mid+1,r,rs); } void pushup(int l,int r,int nod) { int mid = (l + r) >> 1; sum[nod] = max(max(sum[ls],sum[rs]),rsum[ls] + lsum[rs]); lsum[nod] = lsum[ls]; rsum[nod] = rsum[rs]; if (lsum[ls] == (mid - l + 1)) lsum[nod] = lsum[ls] + lsum[rs]; if (rsum[rs] == (r - mid)) rsum[nod] = rsum[rs] + rsum[ls]; } void pushdown(int l,int r,int nod) { int mid = (l + r) >> 1; sum[ls] = lsum[ls] = rsum[ls] = (lazy[nod]) * (mid - l + 1); sum[rs] = lsum[rs] = rsum[rs] = (lazy[nod]) * (r - mid); lazy[ls] =lazy[rs] = lazy[nod]; lazy[nod] = -1; } void modify(int l,int r,int x,int y,int z,int nod) { if (x <= l && y >= r) { sum[nod] = lsum[nod] = rsum[nod] = (r-l+1)*z; lazy[nod] = z; return ; } if (lazy[nod] != -1) { pushdown(l,r,nod); } int mid = (l + r) >> 1; if (x <= mid) modify(l,mid,x,y,z,ls); if (y > mid) modify(mid+1,r,x,y,z,rs); pushup(l,r,nod); } int query(int l,int r,int siz,int nod) { int mid = (l + r) >> 1; if (l == r && siz == 1) return l; if (lazy[nod] != -1) pushdown(l,r,nod); if (sum[nod] >= siz) { if (sum[ls] >= siz) { return query(l,mid,siz,ls); } else if (rsum[ls] + lsum[rs] >= siz) { return mid + 1 - rsum[ls]; } else return query(mid+1,r,siz,rs); } return 0; } int main() { ios::sync_with_stdio(false); int n,m; cin >> n >> m; build(1,n,1); while (m--) { int op,x,y; cin >> op; if (op == 1) { cin >> x; int l = query(1,n,x,1); cout << l << endl; if (!l) continue; modify(1,n,l,l+x-1,0,1); } else { cin >> x >> y; modify(1,n,x,x+y-1,1,1); } } return 0; }

第二類:

要求求解的連續區間是最大的

因為要求的連續區間是最大的,所以我們需要維護四個變數 sum , lmax , rmax , ans 分別代表區間和,左邊連續區間的最大值,右邊連續區間的最大值,整個區間的連續區間的最大值

因為我們要求的是連續的區間,所以我們 pushup 操作的時候也需要確保我們的區間是連續的

lmax 維護: 左區間的lmax和整個左區間 + 右區間的lmax 中的最大值

這類問題的典型例題:

P4513 小白逛公園

題目連結:https://www.luogu.com.cn/problem/P4513

#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <cmath>
#include <cstdio>
#include <iomanip>
#include <ctime>
#include <bitset>
#include <cmath>
#include <sstream>
#include <iostream>
#include <unordered_map>

#define ll long long
#define ull unsigned long long
#define ls nod<<1
#define rs (nod<<1)+1
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 0x3f3f3f3f
#define max(a, b) (a>b?a:b)
#define min(a, b) (a<b?a:b)


const double eps = 1e-10;
const int maxn = 5e5 + 10;
const ll MOD = 99999999999999;

int sgn(double a) { return a < -eps ? -1 : a < eps ? 0 : 1; }

using namespace std;

// #define int ll

int a[maxn];
struct segment_tree {
    int l,r;
    int sum,lmax,rmax,ans;
}tree[maxn << 2];


void pushup(int nod) {
    tree[nod].sum = tree[ls].sum + tree[rs].sum;
    tree[nod].lmax = max(tree[ls].lmax,tree[ls].sum + tree[rs].lmax);
    tree[nod].rmax = max(tree[rs].rmax,tree[rs].sum + tree[ls].rmax);
    tree[nod].ans = max(max(tree[ls].ans,tree[rs].ans),tree[ls].rmax + tree[rs].lmax);
}

void build(int l,int r,int nod) {
    tree[nod].l = l,tree[nod].r = r;
    if (l == r) {
        tree[nod].ans = tree[nod].lmax = tree[nod].rmax = tree[nod].sum = a[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    pushup(nod);
}

void modify(int k,int z,int nod) {
    int l = tree[nod].l,r = tree[nod].r;
    if (l == r) {
        tree[nod].ans = tree[nod].lmax = tree[nod].rmax = tree[nod].sum = z;
        return ;
    }
    int mid = (l + r) >> 1;
    if (k <= mid)
        modify(k,z,ls);
    if (k > mid)
        modify(k,z,rs);
    pushup(nod);
}

segment_tree query(int x,int y,int nod) {
    int l = tree[nod].l,r = tree[nod].r;
    if (x <= l && y >= r)
        return tree[nod];
    int mid = (l + r) >> 1;
    if (y <= mid)
        return query(x,y,ls);
    else if (x > mid)
        return query(x,y,rs);
    else {
        segment_tree le = query(x,y,ls),ri = query(x,y,rs),end;
        end.sum = le.sum + ri.sum;
        end.lmax = max(le.lmax,le.sum + ri.lmax);
        end.rmax = max(ri.rmax,ri.sum + le.rmax);
        end.ans = max(max(le.ans,ri.ans),le.rmax + ri.lmax);
        return end;
    }
}

int main() {
    ios::sync_with_stdio(false);
    int n,m;
    cin >> n >> m;
    for (int i = 1;i <= n;i++)
        cin >> a[i];
    build(1,n,1);
    while (m--) {
        int op,x,y;
        cin >> op >> x >> y;
        if (op == 1 && x > y)
            swap(x,y);
        if (op == 1)
            cout << query(x,y,1).ans << endl;
        else
            modify(x,y,1);
    }
    return 0;
}