[kuangbin] 專題7 線段樹 題解 + 總結
阿新 • • 發佈:2020-08-01
[kuangbin]
專題7 線段樹 題解 + 總結
kuangbin帶你飛
:點選進入新世界
因為專題是後面製作,所以可能部分題目題解會需要轉去我的另一篇題解記錄上
總結:
待補。。。
目錄
- 總結:
- 1. 敵兵佈陣
- 2. I Hate It
- 3.A Simple Problem with Integers
- 4.Mayor's posters
- 5. Just a Hook
- 6.Count the Colors
- 7. Balanced Lineup
- 8. Can you answer these queries?
1. 敵兵佈陣
思路:
基礎模板題
三種思路:維護一個字首陣列,但容易超時、樹狀陣列、線段樹
2. I Hate It
題意理解很簡單,這裡就說下注意點,陣列要開大點不然容易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
思路:模板題,別敲錯板子就行(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
線段樹 + 離散化
https://www.cnblogs.com/RioTian/p/13410156.html
5. Just a Hook
題意:
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
題解: https://www.cnblogs.com/RioTian/p/13410855.html
7. Balanced Lineup
搭配註釋理解
#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?
線段樹,保留有用的功能即可。
#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("");
}
}