题集(线段树1 & 离散化模版)

A 敌兵布阵 (HDU - 1166)

题目链接
(线段树 或 树状数组)

线段树模板题
树状数组单点更新和区间查询

线段树代码:

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
const int M = 50005;
int tree[M << 2], num[M];
void build(int rt, int l, int r) {
	if (l == r) {
		tree[rt] = num[l];
		return;
	}
	int mid = l + r >> 1;
	build(rt << 1, l, mid);
	build(rt << 1 | 1, mid + 1, r);
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
void update(int rt, int x, int num, int L, int R) {
	if (L == R) {
		tree[rt] += num;
		return;
	}
	int mid = L + R >> 1;
	if (x <= mid) update(rt << 1, x, num, L, mid);
	else update(rt << 1 | 1, x, num, mid + 1, R);
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
int query(int rt, int l, int r, int L, int R) {
	if (L > r || R < l) return 0;
	if (L >= l && R <= r) return tree[rt];
	int mid = L + R >> 1;
	int ans = 0;
	ans += query(rt << 1, l, r, L, mid);
	ans += query(rt << 1 | 1, l, r, mid + 1, R);
	return ans;
}
int main() {
	int t;
	scanf("%d", &t);
	int cnt = 1;
	while (t--) {
		int n;
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) scanf("%d", &num[i]);
		build(1, 1, n);
		printf("Case %d:\n", cnt++);
		string str;
		while (cin >> str) {
			if (str == "End") break;
			if (str == "Add") {
				int x, num;
				scanf("%d%d", &x, &num);
				update(1, x, num, 1, n);
			}
			else if (str == "Sub") {
				int x, num;
				scanf("%d%d", &x, &num);
				update(1, x, -num, 1, n);
			}
			else if (str == "Query") {
				int l, r;
				scanf("%d%d", &l, &r);
				printf("%d\n", query(1, l, r, 1, n));
			}
		}
	}
}

树状数组代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int M = 50005;
int n;
int num[M], tree[M << 2];
int lowbit(int x) { return x & (-x); }
void update(int x, int num) {
	for (int i = x; i <= n; i += lowbit(i)) tree[i] += num;
}
int query(int l, int r) {
	int ans1 = 0;
	for (int i = r; i; i -= lowbit(i)) ans1 += tree[i];
	int ans2 = 0;
	for (int i = l - 1; i; i -= lowbit(i)) ans2 += tree[i];
	return ans1 - ans2;
}
int main() {
	int t;
	int cnt = 1;
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
		memset(tree, 0, sizeof(tree));
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &num[i]);
			update(i, num[i]);
		}
		string str;
		printf("Case %d:\n", cnt++);
		while (cin >> str) {
			if (str == "End") break;
			if (str == "Add") {
				int x, num;
				scanf("%d%d", &x, &num);
				update(x, num);
			}
			else if (str == "Sub") {
				int x, num;
				scanf("%d%d", &x, &num);
				update(x, -num);
			}
			else if (str == "Query") {
				int l, r;
				scanf("%d%d", &l, &r);
				printf("%d\n", query(l, r));
			}
		}
	}
}

B Lost Cows(POJ - 2182)

题目链接

线段树: 节点为1代表节点编号还未存在,节点为0待变编号存在,从后往前看起
输入样例中: 1 2 1 0 从后往前看
先看0,从[1, n]中找出未被选中过的第1小的数 [1, 2, 3, 4, 5]选中了1 再将1设为被选中 [2, 3, 4, 5]
再看1,从[1, n]中找出未被选中过的第2小的数 [2, 3, 4, 5]选中了3 再将3设为被选中 [2, 4, 5]
再看2,从[1, n]中找出未被选中过的第3小的数[2, 4, 5]选中了5 再将5设为被选中 [2, 4]
再看1, 从[1, n]中找出未被选中过的第2小的数[2, 4]选中了4 再将4设为被选中 [2]
可以以线段树节点编号作为奶牛编号,节点为1设置为未被选中, 节点为0设置为被选中,每次通过单点查询,查询到相应的编号。

树状数组: 思路大致相同,利用二分查找确定个数

线段树代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int M = 100010;
int num[M], ans[M];
int tree[M << 2];
void build(int rt, int l, int r) {
	if (l == r) {
		tree[rt] = 1;
		return;
	}
	int mid = l + r >> 1;
	build(rt << 1, l, mid);
	build(rt << 1 | 1, mid + 1, r);
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
int query(int rt, int l, int r, int key) {
	if (l == r) {
		tree[rt] = 0;
		return l;
	}
	int mid = l + r >> 1;
	if (tree[rt << 1] >= key) {
		int ans = query(rt << 1, l, mid, key);
		tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
		return ans;
	}
	else if (tree[rt << 1] < key) {
		int ans = query(rt << 1 | 1, mid + 1, r, key - tree[rt << 1]);
		tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
		return ans;
	}
}
int main() {
	int n;
	scanf("%d", &n);
	for (int i = 2; i <= n; ++i) scanf("%d", &num[i]);
	build(1, 1, n);
	for (int i = n; i >= 2; --i) {
		ans[i] = query(1, 1, n, num[i] + 1);
	}
	ans[1] = query(1, 1, n, 1);
	for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
	return 0;
}

树状数组代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int M = 10010;
int n;
int num[M], tree[M << 2], ans[M];

int lowbit(int x) { return x & (-x); }
void update(int x, int num) {
	for (int i = x; i <= n; i += lowbit(i)) {
		tree[i] += num;
	}
}

int sum(int num) {
	int ans = 0;
	for (int i = num; i >= 1; i -= lowbit(i)) {
		ans += tree[i];
	}
	return ans;
}
int main() {
	scanf("%d", &n);
	for (int i = 2; i <= n; ++i) scanf("%d", &num[i]);
	for (int i = 1; i <= n; ++i) {
		update(i, 1);
	}
	for (int i = n; i >= 2; --i) {
		int l = 1, r = n, mid;
		while (l < r) {
			mid = l + r >> 1;
			if (sum(mid) >= num[i] + 1) r = mid;
			else l = mid + 1;
		}
		ans[i] = l;
		update(l, -1);
	}
	int l = 1, r = n, mid;
	while (l < r) {
		mid = l + r >> 1;
		if (sum(mid) >= 1) r = mid;
		else l = mid + 1;
	}
	ans[1] = l;
	for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
	return 0;
}

C Mayor’s posters(POJ - 2528离散化模版)

题目链接
(线段树 + 离散化)

本题因为离散化,所以要开正常线段树双倍的点所以是8倍需要注意
(离散化模版)
线段树节点保存的是颜色,最终查询整个区间的颜色种数,看当前区间的颜色是否被查询过,未被查询过则颜色种数加1

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int M = 10010;

int f[M << 1], ff[M << 1];
int l[M], r[M], tree[M << 3];
bool vis[M << 1];

int find(int x, int r_max) {
    int l = 0, r = r_max;
    while (l <= r) {
        int mid = l + r >> 1;
        if (ff[mid] > x) r = mid;
        else if (ff[mid] < x) l = mid + 1;
        else if (ff[mid] == x) return mid;
    }
}

int query_line(int rt, int l, int r, int L, int R) {
    if (L > r || R < l) return 0;
    if (L >= l && R <= r) {
        if (tree[rt]) {
            if (!vis[tree[rt]]) {
                vis[tree[rt]] = true;
                return 1;
            }
            return 0;
        }
    }
    int ans = 0;
    int mid = L + R >> 1;
    ans += query_line(rt << 1, l, r, L, mid);
    ans += query_line(rt << 1 | 1, l, r, mid + 1, R);
    return ans;
}

void pushdown(int rt) {
    if (tree[rt]) {
        tree[rt << 1] = tree[rt];
        tree[rt << 1 | 1] = tree[rt];
        tree[rt] = 0;
    }
}

void update_line(int rt, int l, int r, int L, int R, int num) {
    if (L > r || R < l) return;
    if (L >= l && R <= r) {
        tree[rt] = num;
        return;
    }
    int mid = L + R >> 1;
    pushdown(rt);
    update_line(rt << 1, l, r, L, mid, num);
    update_line(rt << 1 | 1, l, r, mid + 1, R, num);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        memset(vis, false, sizeof(vis));
        memset(tree, 0, sizeof(tree));
        int n;
        scanf("%d", &n);
        int cnt = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%d%d", &l[i], &r[i]);
            f[cnt++] = l[i];
            f[cnt++] = r[i];
        }
        sort(f, f + cnt);
        ff[1] = f[0];
        int tot = 2;
        for (int i = 1; i < cnt; ++i) {
            if (f[i] != f[i - 1]) {
                ff[tot++] = f[i];
            }
        }
        int max_r = tot - 1;
        int l_node, r_node;
        for (int i = 1; i <= n; ++i) {
            l_node = find(l[i], max_r);
            r_node = find(r[i], max_r);
            update_line(1, l_node, r_node, 1, max_r, i);
        }
        int ans = query_line(1, 1, max_r, 1, max_r);
        printf("%d\n", ans);
    }
    return 0;
}

posted on 2022-06-27 08:29  wxz0v0  阅读(13)  评论(0)    收藏  举报

导航