【参考代码】mz线段树基本操作训练

QAQ为什么高一小朋友们的题都这么难。。

代码仅供参考,禁止照抄哦 = v =


说明一下:read()函数是读入优化,你们直接写scanf就行了。namespace只是为了封装,可以不要。

然后我线段树的姿势可能和你们老师讲的不太一样,个人觉得这样写起来简洁一些,而且效率高。


Promble A

原题是hdu1166。简单的点修改和区间查询。因为太水了我就写的树状数组 = =

#include <cstdio>
#include <iostream>
#include <cstring> 
#define lowbit(i) (i & -i)

using namespace std;

int read()
{
	int sign = 1, n = 0; char c = getchar();
	while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
	while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
	return sign*n;
}

const int Nmax = 50005;

int N, a[Nmax];

namespace BIT {
	int d[Nmax];
	
	inline void init() { memset(d + 1, 0, sizeof(d[0]) * N); }
	
	inline void Add(int pos, int x) 
	{
		for (int i = pos; i <= N; i += lowbit(i)) 
			d[i] += x;
	}
	
	inline int Query(int pos)
	{
		int res = 0;
		for (int i = pos; i; i -= lowbit(i)) res += d[i];
		return res;
	}
}

int main()
{
	for (int T = read(), cas = 1; T --; ++ cas) {
		N = read();
		for (int i = 1; i <= N; ++ i) a[i] = read();
	
		using namespace BIT; init();
		for (int i = 1; i <= N; ++ i) Add(i, a[i]);
		
		char s[10]; printf("Case %d:\n", cas);
		while (~scanf("%s", s) && s[0] != 'E') {
			int i = read(), j = read();
			if (s[0] == 'A') Add(i, j);
			else if (s[0] == 'S') Add(i, -j);
			else printf("%d\n", Query(j) - Query(i - 1));
		}
	}
	
	return 0;
}


Problem B 

原题是hdu1754。记录最大值即可。

#include <cstdio>
#include <iostream>
#include <algorithm>

#define lc (u << 1)
#define rc (u << 1 | 1)

using namespace std;

int read()
{
	int sign = 1, n = 0; char c = getchar();
	while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
	while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
	return sign*n;
}

const int Nmax = 200005;

int N, M;
int a[Nmax];

namespace SegmentTree {
	int mmax[Nmax << 2];
	
	inline void build(int u, int l, int r)
	{
		if (l == r) {
			mmax[u] = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		build(lc, l, mid); build(rc, mid + 1, r);
		mmax[u] = max(mmax[lc], mmax[rc]);
	}
	
	inline void update(int u, int l, int r, const int &pos, const int &x)
	{
		if (l == r) {
			mmax[u] = x;
			return;
		}
		int mid = (l + r) >> 1;
		if (pos <= mid) update(lc, l, mid, pos, x);
		else update(rc, mid + 1, r, pos, x);
		mmax[u] = max(mmax[lc], mmax[rc]);
	}
	
	inline int Query(int u, int l, int r, int L, int R)
	{
		if (l == L && r == R) return mmax[u];
		int mid = (l + r) >> 1;
		if (R <= mid) return Query(lc, l, mid, L, R);
		else if (L > mid) return Query(rc, mid + 1, r, L, R);
		return max(Query(lc, l, mid, L, mid), Query(rc, mid + 1, r, mid + 1, R));
	}
}

int main()
{
	while (~scanf("%d%d", &N, &M)) {
		for (int i = 1; i <= N; ++ i) a[i] = read();
		using namespace SegmentTree;
		build(1, 1, N); 
		
		char c; int A, B;
		while (M --) {
			scanf(" %c", &c); A = read(); B = read();
			if (c == 'Q') printf("%d\n", Query(1, 1, N, A, B));
			else update(1, 1, N, A, B);
		}
	}
	
	return 0;
}

Problem C

原题hdu1698。记录当前区间的颜色即可,如果不止一种颜色,就标记为0。修改的时候记得下放标记。

#include <cstdio>
#include <iostream>

#define lc (u << 1)
#define rc (u << 1 | 1)

using namespace std;

int read()
{
	int sign = 1, n = 0; char c = getchar();
	while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
	while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
	return sign*n;
}

const int Nmax = 100005;

namespace SegmentTree {
	int lazy[Nmax << 2];
	
	void build(int u, int l, int r) 
	{
		lazy[u] = 1; if (l == r) return; 
		int mid = (l + r) >> 1;
		build(lc, l, mid); build(rc, mid + 1, r);
	} 
	
	inline void Modify(int u, int l, int r, int L, int R, const int &x) 
	{
		if (lazy[u] == x || (l == L && r == R)) { lazy[u] = x; return; }
		if (lazy[u]) { lazy[lc] = lazy[rc] = lazy[u]; lazy[u] = 0; }
		int mid = (l + r) >> 1;
		if (R <= mid) Modify(lc, l, mid, L, R, x);
		else if (L > mid) Modify(rc, mid + 1, r, L, R, x);
		else {
			Modify(lc, l, mid, L, mid, x);
			Modify(rc, mid + 1, r, mid + 1, R, x);
		}
	}
	
	inline int Query(int u, int l, int r) 
	{
		if (lazy[u]) return (r - l + 1) * lazy[u];
		int mid = (l + r) >> 1;
		return Query(lc, l, mid) + Query(rc, mid + 1, r); 
	}
}

int N, Q;

int main()
{
	for (int T = read(), cas = 1; T --; ++ cas) {
		N = read(), Q = read(); 
		using namespace SegmentTree;
		build(1, 1, N);
		
		for (int l, r, x; Q --; ) {
			l = read(), r = read(), x = read();
			Modify(1, 1, N, l, r, x);
		}
		printf("Case %d: The total value of the hook is %d.\n", cas, Query(1, 1, N));
	}
	return 0;
}

Problem D

原题hdu3308。记录左边连续上升的长度,右边连续上升的长度和整个区间连续上升的长度。
Pushup的时候合并一下就行了,然后Query横跨两个区间的时候也要考虑是否可以连续。

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

int read()
{
	int sign = 1, n = 0; char c = getchar();
	while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
	while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
	return sign*n;
}

const int Nmax = 100005;

int N, M;
int a[Nmax];

#define lc (u << 1)
#define rc (u << 1 | 1)

namespace SegmentTree {
	int lsum[Nmax << 2], rsum[Nmax << 2], msum[Nmax << 2];
	
	inline void Push_up(int u, int l, int r, int m)
	{
		if (a[m] < a[m + 1]) {
			lsum[u] = (lsum[lc] == m - l + 1) ? (m - l + 1 + lsum[rc]) : lsum[lc];
			rsum[u] = (rsum[rc] == r - m) ? (r - m + rsum[lc]) : rsum[rc];
			msum[u] = max(max(msum[lc], msum[rc]), lsum[rc] + rsum[lc]);
		} else {
			lsum[u] = lsum[lc]; rsum[u] = rsum[rc];
			msum[u] = max(msum[lc], msum[rc]);
		} 
	}
	
	void build(int u, int l, int r)
	{
		if (l == r) {
			lsum[u] = rsum[u] = msum[u] = 1;
			return;
		}
		int mid = (l + r) >> 1;
		build(lc, l, mid); build(rc, mid + 1, r);
		Push_up(u, l, r, mid);
	}
	
	void Modify(int u, int l, int r, const int &pos, const int &x)
	{
		if (l == r) { a[pos] = x; return; }
		int mid = (l + r) >> 1;
		if (pos <= mid) Modify(lc, l, mid, pos, x);
		else Modify(rc, mid + 1, r, pos, x);
		Push_up(u, l, r, mid);
	}
	
	int Query(int u, int l, int r, int L, int R) 
	{
		if (L == l && R == r) return msum[u];
		int mid = (l + r) >> 1;
		if (R <= mid) return Query(lc, l, mid, L, R);
		if (L > mid) return Query(rc, mid + 1, r, L, R);
		int res = max(Query(lc, l, mid, L, mid), Query(rc, mid + 1, r, mid + 1, R));
		if (a[mid] < a[mid + 1]) res = max(res, min(mid - L + 1, rsum[lc]) + min(R - mid, lsum[rc]));
		return res;
	}
}

int main()
{
	for (int T = read(); T --; ) {
		N = read(), M = read();
		for (int i = 1; i <= N; ++ i) a[i] = read();
		
		using namespace SegmentTree;
		build(1, 1, N); char s[10]; int a, b;
		while (M --) {
			scanf("%s", s); a = read(); b = read();
			if (s[0] == 'Q') printf("%d\n", Query(1, 1, N, a + 1, b + 1));
			else Modify(1, 1, N, a + 1, b);
		}
	}
	return 0;
}


Problem E

原题hdu1542。扫描线入门。建议以后要准备省选&NOI的同学掌握。
具体做法请自行百度。建议自学,这种东西我估计老师也没法给你们讲懂的。
另外用到了离散化,这个也要先去学习一下。

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int Lmax = 205;

int N;
struct Line {
	double x, down, up;
	int cover;
	bool operator < (const Line &b) const { return x < b.x; }
}L[Lmax];
double h[Lmax];

#define lc (u << 1)
#define rc (u << 1 | 1)

namespace SegmentTree {
	int cover[Lmax << 2]; double len[Lmax << 2];
	
	inline void build(int u, int l, int r) 
	{
		cover[u] = len[u] = 0;
		if (l == r) return;
		int mid = (l + r) >> 1;
		build(lc, l, mid); build(rc, mid + 1, r);
	}
	
	inline void Push_up(int u, int l, int r)
	{
		if (cover[u]) len[u] = h[r] - h[l - 1];
		else len[u] = (l == r) ? 0 : len[lc] + len[rc];
	}
	
	void Update(int u, int l, int r, int L, int R, const int &x)
	{
		if (l == L && r == R) {
			cover[u] += x;
			Push_up(u, l, r); return;
		} 
		int mid = (l + r) >> 1;
		if (R <= mid) Update(lc, l, mid, L, R, x);
		else if (L > mid) Update(rc, mid + 1, r, L, R, x);
		else {
			Update(lc, l, mid, L, mid, x);
			Update(rc, mid + 1, r, mid + 1, R, x);
		}
		Push_up(u, l, r);
	}
}

int main()
{
	ios :: sync_with_stdio(false);
	
	int cas = 0, M; 
	while (cin >> N && N) {
		M = 0; double x1, x2, y1, y2;
		for (int i = 1; i <= N; ++ i) {
			cin >> x1 >> y1 >> x2 >> y2;
			h[M] = y1;
			L[M ++] = (Line) { x1, y1, y2, 1 };
			h[M] = y2;
			L[M ++] = (Line) { x2, y1, y2, -1 };
		}
		sort(h, h + M);
		N = unique(h, h + M) - h; 
		sort(L, L + M); -- M;
		printf("Test case #%d\n", ++cas);
		
		using namespace SegmentTree; 
		double ans = 0. ; build(1, 1, N - 1);
		for (int i = 0; i < M; ++ i) {
			int l = lower_bound(h, h + N, L[i].down) - h + 1;
			int r = lower_bound(h, h + N, L[i].up) - h;
			Update(1, 1, N - 1, l, r, L[i].cover);
			ans += len[1] * (L[i + 1].x - L[i].x);
		}
		printf("Total explored area: %0.2f\n\n", ans);
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值