1. 程式人生 > 實用技巧 >[kuangbin] 專題7 線段樹 題解 + 總結

[kuangbin] 專題7 線段樹 題解 + 總結

[kuangbin] 專題7 線段樹 題解 + 總結

kuangbin帶你飛點選進入新世界

線段樹相關知識:樹狀陣列線段樹離散化

因為專題是後面製作,所以可能部分題目題解會需要轉去我的另一篇題解記錄上


總結:

待補。。。

目錄

1. 敵兵佈陣

HDU - 1166

思路:

基礎模板題

三種思路:維護一個字首陣列,但容易超時、樹狀陣列、線段樹

題解連結

2. I Hate It

HDU - 1754

題意理解很簡單,這裡就說下注意點,陣列要開大點不然容易RE,同時因為資料較大,可以採用快讀或者scanf

#include<bits/stdc++.h>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const int inf = 1<<30;
const LL maxn = 200000+5;

int N, M, a[maxn];
int maxA[maxn*4];
void pushup(int id){maxA[id] = max(maxA[id<<1], maxA[id<<1|1]);}
void build(int id, int l, int r){
    if(l==r){
        maxA[id] = a[l];
        return;
    }
    int mid = (l+r)>>1;
    build(id<<1, l, mid);
    build(id<<1|1, mid+1, r);
    pushup(id);
}
void update(int id, int l, int r, int x, int v){
    if(l==r){
        maxA[id] = v;
        return;
    }
    int mid = (l+r)>>1;
    if(x<=mid) update(id<<1, l, mid, x, v);
    else update(id<<1|1, mid+1, r, x, v);
    pushup(id);
}
int query(int id, int l, int r, int x, int y){
    if(x<=l && y>=r) return maxA[id];
    int mid = (l+r)>>1, ret = -inf;
    if(x <= mid) ret = max(ret, query(id<<1, l, mid, x, y));
    if(y > mid)  ret = max(ret, query(id<<1|1, mid+1, r, x, y));
    return ret;
}
int main()
{
    char opt;
    int x, y;
    while(scanf("%d%d",&N,&M)!=EOF){
        ms(a, 0);
        fill(maxA, maxA+maxn, -inf);
        for(int i = 1; i <= N; i++)
            scanf("%d",&a[i]);
        build(1, 1, N);
        while(M--){
            scanf(" %c%d%d",&opt,&x,&y);
            if(opt=='Q')
                printf("%d\n",query(1, 1, N, x, y));
            else if(opt=='U')
                update(1, 1, N, x, y);
        }
    }

	return 0;
}

3.A Simple Problem with Integers

POJ - 3468

思路:模板題,別敲錯板子就行(WA好幾次2333)

#include <stdio.h>
using namespace std;
typedef long long ll;
const ll inf = 4e18+10;
const int mod = 1000000007;
const int mx = 5e6+5; 
ll sum[mx], add[mx];
void pushup(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushdown(int rt, int m) {//更新rt的子節點
    if (add[rt]) {
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        sum[rt << 1] += (m - (m >> 1))* add[rt];
        sum[rt << 1 | 1] += (m >> 1)* add[rt];
        add[rt] = 0;//取消本層標記
    }
}
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void biuld(int l, int r, int rt) {//用滿二叉樹建樹
    add[rt] = 0;
    if (l == r) {//葉子結點,賦值
        scanf("%lld", &sum[rt]);
        return;
    }
    int mid = (l + r) >> 1;
    biuld(lson);
    biuld(rson);
    pushup(rt);//向上更新區間和
}
void update(int a, int b, ll c, int l, int r, int rt) {//區間更新
    if (a <= l && b >= r) {
        sum[rt] += (r - l + 1) * c;
        add[rt] += c;
        return;
    }
    pushdown(rt, r - l + 1);//向下更新
    int mid = (l + r) >> 1;
    if (a <= mid)update(a, b, c, lson);//分成兩半深入
    if (b > mid)update(a, b, c, rson);
    pushup(rt);
}
ll query(int a, int b, int l, int r, int rt) {//區間求和
    if (a <= l && b >= r)
        return sum[rt];//滿足lazy直接返回
    pushdown(rt, r - l + 1);
    int mid = (l + r) >> 1;
    ll ans = 0;
    if (a <= mid)ans += query(a, b, lson);
    if (b > mid)ans += query(a, b, rson);
    return ans;
}
int main()
{
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, m;
    scanf("%d", &n); scanf("%d", &m);
    biuld(1, n, 1);
    while (m--) {
        char str[2];
        int a, b;
        ll c;
        scanf("%s", str);
        if (str[0] == 'C') {
            scanf("%d", &a); scanf("%d", &b); scanf("%lld", &c);
            update(a, b, c, 1, n, 1);
        }
        else {
            scanf("%d", &a); scanf("%d", &b);
            printf("%lld\n", query(a, b, 1, n, 1));
        }
    }
    return 0;
}

4.Mayor's posters

POJ - 2528

線段樹 + 離散化

https://www.cnblogs.com/RioTian/p/13410156.html

5. Just a Hook

HDU - 1698

題意:

N個數, 初始全部為1, 進行Q次以下操作
把[l, r]的所有值改為V (1<=V<=3)
求N個數的和.

題解:

套上線段樹區間更新的板子, 把求和的+= 改為 =

#include<bits/stdc++.h>
using namespace std;
#define sc(n) scanf("%c",&n)
#define sd(n) scanf("%d",&n)
#define pd(n) printf("%d\n", (n))
#define sdd(n,m) scanf("%d %d",&n,&m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define pdd(n,m) printf("%d %d\n",n, m)
#define ms(a,b) memset(a,b,sizeof(a))
#define all(c) c.begin(),c.end()
#define pb push_back  
#define fi first  
#define se second  
#define mod(x) ((x)%MOD)
#define lowbit(x) (x & (-x))
#define gcd(a,b) __gcd(a,b)

typedef long long ll;
typedef pair<int, int> PII;
typedef vector<int> VI;
typedef vector<string> VS;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;

int t, n, m, x, y, z, sum[maxn << 2], ans;

void pushdown(int p) {
	if (sum[p] == -1)return;
	sum[p << 1] = sum[p << 1 | 1] = sum[p];
	sum[p] = -1;
}

void update(int id, int l, int r, int x, int y, int z) {
	if (x <= l && r <= y) {
		sum[id] = z; return;
	}
	pushdown(id);
	int m = (l + r) >> 1;
	if (x <= m)update(id << 1, l, m, x, y, z);
	if (y > m) update(id << 1 | 1, m + 1, r, x, y, z);
}

void query(int id, int l, int r) {
	if (sum[id] != -1) {
		ans += sum[id] * (r - l + 1);
		return;
	}
	int mid = (l + r) >> 1;
	query(id << 1, l, mid);
	query(id << 1 | 1, mid + 1, r);
}

int main() {
	//freopen("in.txt", "r", stdin);
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int Kcase = 1;
	sd(t); while (t--) {
		ans = 0; ms(sum, -1);
		sdd(n, m); sum[1] = 1;
		while (m--) {
			sddd(x, y, z);
			update(1, 1, n, x, y, z);
		}
		query(1, 1, n);
		printf("Case %d: The total value of the hook is %d.\n", Kcase++, ans);
	}
	return 0;
}

6.Count the Colors

ZOJ - 1610

題解: https://www.cnblogs.com/RioTian/p/13410855.html

7. Balanced Lineup

POJ - 3264

搭配註釋理解

#include <iostream>
#include <cstdio>
using namespace std;
typedef pair<int, int > PII;
const int N = 50000 + 10, M = N * 4;
struct Node {
    // 不用開long long 
    int l, r, hei, low;
} root[M];
void pushup(int now) {
    // 記錄最高的和最矮的牛的高度 
    root[now].hei = max(root[now << 1].hei, root[now << 1 | 1].hei);
    root[now].low = min(root[now << 1].low, root[now << 1 | 1].low);
}
void build(int now, int left, int right) {
    root[now].l = left, root[now].r = right;
    if (left == right) {
        // 直接輸入葉子節點的值,很方便,而且省空間 
        scanf("%d", &root[now].hei);
        // 預設葉子節點的牛既是最高的,也是最矮的 
        root[now].low = root[now].hei;
        return; 
    }
    int mid = (left + right) >> 1;
    int ln = now << 1, rn = now << 1 | 1;
    build(ln, left, mid);
    build(rn, mid + 1, right);
    pushup(now);
}
// 甚至不用寫update和pushdown...
PII query(int now, int L, int R) {
    // 找到了要的區間返回這個區間的最大值和最小值,用來和後面的區間進行對比 
    if (L <= root[now].l && root[now].r <= R) return PII(root[now].hei, root[now].low);
    // 規定first為最大值,second為最小值,那麼沒找到就返回一個極端值就行了 
    if (L > root[now].r || R < root[now].l) return PII(-1e9, 1e9);
    // 不用pushdown
    // 找到左右子樹的最大值和最小值對,然後對比 
    PII nhei = query(now << 1, L, R);
    PII nlow = query(now << 1 | 1, L ,R); 
    // 對比左區間的最大值和右區間的最大值,左區間的最小值和右區間的最小值... 
    return PII(max(nhei.first, nlow.first), min(nhei.second, nlow.second));
}
int main() {
    int n, m, l ,r;
    scanf("%d%d", &n, &m);
    build(1, 1, n);
    while (m--) {
        scanf("%d%d", &l, &r);
        PII res = query(1, l, r);
        printf("%d\n", res.first - res.second);
    }
    return 0;
}

8. Can you answer these queries?

HDU - 4027

線段樹,保留有用的功能即可。

#include<bits/stdc++.h>
typedef long long ll;
#define mid (l+r)/2
#define lch in*2
#define rch in*2+1
const int maxn = 1e5 + 9;
int N, M, L, R;
ll tr[maxn * 4] = {};
void build(int in = 1, int l = 1, int r = N) {
    if (l == r) return void(scanf("%lld", tr + in));
    build(lch, l, mid); build(rch, mid + 1, r);
    tr[in] = tr[lch] + tr[rch];
}
void update(int in = 1, int l = 1, int r = N) {
    if (l > R || r < L || tr[in] == (r - l + 1)) return;
    if (l == r) return void(tr[in] = sqrt(tr[in]));
    update(lch, l, mid); update(rch, mid + 1, r);
    tr[in] = tr[lch] + tr[rch];
}
ll qurry(int in = 1, int l = 1, int r = N) {
    if (l > R || r < L) return 0;
    if (L <= l && R >= r) return tr[in];
    return qurry(lch, l, mid) + qurry(rch, mid + 1, r);
}
void solve() {
    build();
    scanf("%d", &M);
    while (M--) {
        int t; scanf("%d%d%d", &t, &L, &R);
        if (L > R) L ^= R ^= L ^= R;
        if (!t) update();
        else printf("%lld\n", qurry());
    }
}
int main() {
    //freopen("in.txt", "r", stdin);
    for (int __ = 1; ~scanf("%d", &N);) {
        printf("Case #%d:\n", __++);
        solve();
        puts("");
    }
}