kuangbin带你飞----线段树专题一(基础操作,单点,区间更新和查询)


A

题意:给出q个询问,单点更新和查询

主要是注意模板的书写

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
	char ch = getchar(); int x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
const int N = 5e5 + 10;
int num[4 * N];
int lazy[4 * N];
int T;
int n;
void pushup(int root)
{
	num[root] = num[root << 1] + num[root << 1 | 1];
}
void build(int l, int r, int root)
{
	if (l == r) {
		num[root] = 0; return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, root << 1);
	build(mid + 1, r, root << 1 | 1);
	pushup(root);
}
void update(int l, int r, int root, int pos, int val)
{
	if (l == r)
	{
		num[root] += val;
		return;
	}
	int mid = (l + r) >> 1;
	if (pos <= mid)update(l, mid, root << 1, pos, val);
	else update(mid + 1, r, root << 1 | 1, pos, val);
	pushup(root);
}
int querry(int l, int r, int root ,int lf, int rt)
{
	if (lf <= l && r <= rt)return num[root];
	int mid = (l + r) >> 1;
	int ans = 0;
	if (lf <= mid)ans+=querry(l, mid, root << 1, lf, rt);
	if (rt > mid)ans+=querry(mid + 1, r, root << 1 | 1, lf, rt);	
	return ans;
}
int main()
{
	T = read();
	for (int i = 1; i <= T; i++)
	{
		string s;
		n = read();
		build(1, n, 1);
		int x;
		up(i, 0, n)
		{
			x = read(); update(1, n, 1, i + 1, x);
		}
		printf("Case %d:\n", i);
		while (cin >> s && s != "End")
		{
			if (s == "Query")
			{
				int x, y; x = read(), y = read();
				cout << querry(1, n, 1, x, y) << endl;
			}
			if (s == "Add")
			{
				int p, v;
				p = read(), v = read();
				update(1, n, 1, p, v);
			}
			if (s == "Sub")
			{
				int p, v;
				p = read(), v = read();
				update(1, n, 1, p, -v);
			}
		}
	}
	return 0;
}

B

题意:单点更新,区间查询最大值,这种情况需要利用pushup来进行更新,每一次更新回溯到根节点,每一次更改最多被修改logn个点保证时间复杂度。


C

题意:区间更新和查询。

注意 需要灵活运用lazy标记,我们为什么要用lazy标记是因为对于单点更新来说,我们沿着一条链向下更新最多有logn个节点被更新到了,总体nlogn的复杂度,而如果时区间更新我们仍然用以上的更新的方式,那么时间复杂度时nlogn*(每一次区间长度)那么肯定是很容易T掉的,我们加入lazy标记后,每一次查询和添加都只需要log次,标记会随着我们对于这一棵树的遍历而跟随我们遍历,大大减少了时间复杂度。


D

模板,求区间覆盖的不同的个数。我们可以看出实质是对区间进行染色,区间更新,并且整体查询我这里保存了每一种颜色然后查询到叶子节点,看这个颜色是否被访问过了,没有的话答案+1。同样灵活运用lazy标记即可,先进行离散化,然后区间更新。

值得注意的是,我们离散话例如,(1,10),(1,3),(5,10)的区间的时候,如果进行区间染色那么1-3被染成1颜色,5-10被染成2颜色,4的话还有1- 10的颜色,答案为3,如果简单离散化的话,就会出现1-10查询结果为2,以为你1 3 5 10离散成为了1 2 3 4,区间更新了1 2,3 4,可以发现问题出现在了3和5身上,应该在3 5之间加一个数字,3 4 5 的话就不会出现任何问题了。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
	char ch = getchar(); int x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
const int N = 1e4 + 10;
vector<int>vec;
vector<pir>a;
int lazy[N << 4];
int num[N << 4];
int vis[N << 4];
int c,n,ans;
void pushdown(int root)
{
	if (lazy[root])
	{
		lazy[root << 1] = lazy[root];
		lazy[root << 1 | 1] = lazy[root];
		num[root << 1] = lazy[root];
		num[root << 1 | 1] = lazy[root];
		lazy[root] = 0;
	}
}
void build(int l, int r, int root)
{
	lazy[root] = 0;
	num[root] = 0;
	if (l == r) { return; }
	int mid = (l + r) >> 1;
	build(l, mid, root << 1);
	build(mid + 1, r, root << 1 | 1);
}
void update(int l, int r, int root, int val, int lf,int rt)
{
	if (lf <= l && r <= rt)
	{
		num[root] = val;
		lazy[root] = val;
		return;
	}
	pushdown(root);
	int mid = (l + r) >> 1;
	if (lf <= mid)update(l, mid, root << 1, val, lf, rt);
	if (rt > mid)update(mid + 1, r, root << 1 | 1, val, lf, rt);
}
void querry(int l, int r, int root,int pos)
{
	if (l == r)
	{
		if (num[root] == 0)return;
		//cout << "l" << l << "r" << r << endl;
		if (!vis[num[root]])
			ans++, vis[num[root]] = 1;
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(root);
	if (mid < pos)querry(mid + 1, r, root << 1 | 1, pos);
	else querry(l, mid, root << 1, pos);
}
int main()
{
	c = read();
	while (c--)
	{
		ans = 0;
		n = read();
		memset(vis, 0, sizeof(vis));
		a.clear();
		vec.clear();
		int x, y;
		up(i, 0, n)
		{
			x = read(), y = read();
			vec.push_back(x), vec.push_back(y);
			a.push_back(make_pair(x, y));
		}
		sort(vec.begin(), vec.end());
		vec.erase(unique(vec.begin(), vec.end()), vec.end());
		int len = vec.size() - 1;
		up(i, 0, len)
		{
			if (vec[i] < vec[i + 1] - 1)
			{
				vec.push_back(vec[i] + 1);
			}
		}
		sort(vec.begin(), vec.end());
		vec.erase(unique(vec.begin(), vec.end()), vec.end());
		build(1, vec.size(), 1);
		up(i, 0, n)
		{
			int pos1 = lower_bound(vec.begin(), vec.end(), a[i].first) - vec.begin();
			int pos2 = lower_bound(vec.begin(), vec.end(), a[i].second) - vec.begin();
			pos1++, pos2++;
			update(1, vec.size(), 1, i + 1, pos1, pos2);
		}
		upd(i, 1,vec.size())
		{
			querry(1, vec.size(), 1, i);
		}
		cout << ans << endl;
	}
	return 0;
}

E

区间更新整体查询,每一次更新,直接推到整个区间,然后下方标记。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
	char ch = getchar(); int x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
const int N = 1e5 + 10;
int t, n, q;
int tree[N << 2];
int lazy[N << 2];
void pushup(int root)
{
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
void pushdown(int l,int r,int root)
{
	if (lazy[root])
	{
		lazy[root << 1] = lazy[root];
		lazy[root << 1 | 1] = lazy[root];
		int mid = (l + r) >> 1;
		tree[root<<1] = lazy[root] * (mid - l + 1);
		tree[root << 1 | 1] = lazy[root] * (r - mid);
		lazy[root] = 0;
	}
}
void build(int l,int r,int root)
{
	lazy[root] = 0;
	if (l == r)
	{
		tree[root] = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, root << 1);
	build(mid + 1, r, root << 1 | 1);
	pushup(root);
}
void update(int l, int r, int root, int lf, int rt,int val)
{
	if (lf <= l && r <= rt)
	{
		tree[root] = val * (r - l + 1);
		lazy[root] = val; return;
	}
	pushdown(l, r, root);
	int mid = (l + r) >> 1;
	if (lf <= mid)update(l, mid, root << 1, lf, rt, val);
	if(rt>mid) update(mid + 1, r, root << 1 | 1, lf, rt, val);
	pushup(root);
}
int querry(int l, int r, int root, int lf, int rt)
{
	if (lf <= l && r <= rt)
	{
		return tree[root];
	}
	pushdown(l,r,root);
	int mid = (l + r) >> 1;
	int ans = 0;
	if (lf <= mid)ans += querry(l, mid, root << 1, lf, rt);
	if (rt > mid)ans += querry(mid+1, r, root << 1 | 1, lf, rt);
	return ans;
}
int main()
{
	t = read();
	upd(i, 1, t)
	{
		n = read(), q = read();
		build(1, n, 1);
		while (q--)
		{
			int x, y, z;
			x = read(), y = read(), z = read();
			update(1, n, 1, x, y, z);
		}
		printf("Case %d: The total value of the hook is %d.\n", i, querry(1, n, 1, 1, n));
	}
	return 0;
}

F

和之间差不多


G

很有意思的一道题目,每一次区间更新,所有val开根号,区间查询最大值。由于val最大再int范围内,所以开根号最多32次就会变成1,所以我们就下到每一层去更新所有的点,然后区间值为长度(即整个区间每一个值都是1)就跳过不再更新。总体来说上限是32*nlogn的复杂度。

坑点在于输入的x,y大小关系不确定。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
	char ch = getchar(); int x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
const int N = 1e5 + 10;
ll tree[N << 2];
ll len[N << 2];
void pushup(int root)
{
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
//	len[root] = len[root << 1] + len[root << 1 | 1];
}
void build(int l,int r,int root)
{
	tree[root] = 0;
	if (l == r) { scanf("%lld",&tree[root]); return; }
	int mid = (l + r) >> 1;
	build(l, mid, root << 1);
	build(mid + 1, r, root << 1 | 1);
	pushup(root);
}
void update(int l, int r, int root, int lf, int rt)
{
	if (l == r) { tree[root] = (ll)sqrt(tree[root]); return; }
	if (lf <= l && r <= rt&&tree[root]==r-l+1)
	{
		return;
	}
	int mid = (l + r) >> 1;
	if (lf <= mid)update(l, mid, root << 1, lf, rt);
	if (mid < rt)update(mid + 1, r, root << 1 | 1, lf, rt);
	pushup(root);
}
ll querry(int l, int r, int root, int lf, int rt)
{
	if (lf <= l && r <= rt)
	{
		return tree[root];
	}
	int mid = (l + r) >> 1;
	ll ans = 0;
	if (lf <= mid)ans += querry(l, mid, root << 1, lf, rt);
	if (rt > mid)ans += querry(mid + 1, r, root << 1 | 1, lf, rt);
	return ans;
}
int n,m;
int p = 0;
int main()
{
	while (scanf("%d",&n)!=EOF)
	{
		p++;
		ll x,y;
		int t;
		build(1, n, 1);
		m = read();
		printf("Case #%d:\n", p);
		up(i, 0, m)
		{
			t = read();
			scanf("%lld %lld", &x, &y);
			if (x > y)swap(x, y);
			if (t == 1)printf("%lld\n", querry(1, n, 1, x, y));
			if (t == 0)update(1, n, 1, x, y);
		}
		cout << endl;
	}
	return 0;
}

I

题意:每次单点更新摧毁一个点,同样可以复位一个点,区间查询,以这个点为中心向两边发散能走多远。

可以有两种思路,一种是关于这个点左右二分长度,因为具有单调性

第二种就是直接查询,题目相当于求连续区间长度,我们如果用分治思想来考虑的话,包含当前节点的线序区间长度,要么全在左边要么全在右边,要么跨接中间。针对此我们每一次维护都维护当前区间左连续最长区间和右最长连续区间即可中间区间查询通过访问左子树的右连续区间和右子树左连续区间得到,那么三种情况都能访问到了。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
	char ch = getchar(); int x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
const int N = 5e4 + 10;
struct {
	int l, r;
}tree[N<<2];
int n, m;
stack<int >st;
void pushup(int root, int mid)
{
	tree[root].l = tree[root << 1].l;
	tree[root].r = tree[root << 1 | 1].r;
	if (tree[root].l == mid && tree[root << 1 | 1].l)tree[root].l = tree[root << 1 | 1].l;
	if (tree[root].r == mid + 1 && tree[root << 1].r)tree[root].r = tree[root << 1].r;
}
void build(int l, int r, int root)
{
	int mid = (l + r) >> 1;
	if (l == r) {
		tree[root].l = tree[root].r = l;
		return;
	}
	build(l, mid, root << 1);
	build(mid + 1, r, root << 1 | 1);
	pushup(root,mid);
}
void update(int l, int r, int root, int pos,int val)
{
	if (l == r)
	{
		if(val==0)
			tree[root].l = tree[root].r = 0;
		else tree[root].l = tree[root].r = l;
		return;
	}
	int mid = (l + r) >> 1;
	if (mid < pos)update(mid + 1, r, root << 1 | 1, pos, val);
	else update(l, mid, root << 1, pos, val);
	pushup(root,mid);
}
int querry(int l, int r, int root, int pos)
{
	if (l == r) {
		if (tree[root].l)return 1;
		else return 0;
	}
	if (tree[root].l&&tree[root].l >= pos)return tree[root].l - l + 1;
	if (tree[root].r&&tree[root].r <= pos)return r - tree[root].r + 1;
	int sum = 0;
	int mid = (l + r) >> 1;
	if (tree[root << 1].r&&tree[root << 1 | 1].l)
	{
		if (tree[root << 1].r <= pos && pos <= tree[root << 1 | 1].l)
			return tree[root << 1 | 1].l - tree[root << 1].r + 1;
	}
	if (pos <= mid)querry(l, mid, root << 1, pos);
	else querry(mid + 1, r, root << 1 | 1, pos);
}
char s[2];
int main()
{
	while (cin >> n>>m)
	{
		build(1, n, 1);
		while (!st.empty())st.pop();
		up(i, 0, m)
		{
			cin >> s;
			if (s[0] == 'D')
			{
				int x; x = read();
				update(1, n, 1, x, 0);
				st.push(x);
			}
			if (s[0] == 'R')
			{
				if (st.empty())continue;
				int top = st.top();
				st.pop();
				update(1, n, 1, top, 1);
			}
			if (s[0] == 'Q')
			{
				int x; x = read();
				printf("%d\n", querry(1, n, 1, x));
			}
		}
	}
	return 0;
}
posted @ 2019-09-18 19:02  LORDXX  阅读(206)  评论(0编辑  收藏  举报