[kuangbin] 专题7 线段树 题解 + 总结

[kuangbin] 专题7 线段树 题解 + 总结

kuangbin带你飞:点击进入新世界

kuangbin专题十二 基础DP1 题解+总结:https://www.cnblogs.com/RioTian/p/13110438.html

kuangbin专题六 最小生成树 题解+总结:https://www.cnblogs.com/RioTian/p/13380764.html

[kuangbin]专题九 连通图 题解+总结 : https://www.cnblogs.com/RioTian/p/13395039.html

线段树相关知识:树状数组 、 线段树 、离散化

因为专题是后面制作,所以可能部分题目题解会需要转去我的另一篇题解记录上


总结:

待补。。。

1. 敌兵布阵

HDU - 1166

思路:

基础模板题

三种思路:维护一个前缀数组,但容易超时、树状数组、线段树

题解链接

2. I Hate It

HDU - 1754

题意理解很简单,这里就说下注意点,数组要开大点不然容易RE,同时因为数据较大,可以采用快读或者scanf

#include
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 
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
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 PII;
typedef vector VI;
typedef vector 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 
#include 
using namespace std;
typedef pair 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
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("");
    }
}

你可能感兴趣的:([kuangbin] 专题7 线段树 题解 + 总结)