线段树水题几枚

最近接触了一下线段树,按理来说这个半年前就该看了,实际上自己却总想刷水题而躲避难题,不过,该来的还是要来的,既然选择了数据结构,就让各种树来的猛烈些吧。


以下为最初级的线段树,只更新点,没有delay操作


1.HDU 1166 敌兵布阵

这道题用线段树或者树状数组都可以做,HDU上的数据貌似很弱,模拟竟然都能过

首先是线段树版 ,线段树所带信息为当前区间所有点的值的和

/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 50005 #define INF 100000000 #define eps 1e-7 #define L(x) x<<1 #define R(x) x<<1|1 using namespace std; int a[MAXN]; struct node { int left, right, mid; int sum; }tree[4 * MAXN]; int ans; void up(int C) { tree[C].sum = tree[L(C)].sum + tree[R(C)].sum; } void make_tree(int s, int e, int C) { tree[C].left = s; tree[C].right = e; tree[C].mid = (s + e) / 2; tree[C].sum = 0; if(s == e) { tree[C].sum = a[s]; return; } make_tree(s, tree[C].mid, L(C)); make_tree(tree[C].mid + 1, e, R(C)); up(C); } void update(int C, int p, int v) { if(tree[C].left == p && tree[C].right == p) { tree[C].sum += v; return; } if(p <= tree[C].mid) update(L(C), p, v); else update(R(C), p, v); up(C); } void search(int s, int e, int C) { if(s <= tree[C].left && tree[C].right <= e) { ans += tree[C].sum; return; } if(tree[C].mid >= e) search(s, e, L(C)); else if(tree[C].mid < s) search(s, e, R(C)); else { search(s, tree[C].mid, L(C)); search(tree[C].mid + 1, e, R(C)); } } int main() { #ifdef LOCAL freopen("d:/data.in","r",stdin); freopen("d:/data.out","w",stdout); #endif char s[20]; int t, n, i, cas = 0, x, y; scanf("%d", &t); while(t--) { scanf("%d", &n); for(i = 1; i <= n; i++) scanf("%d", &a[i]); make_tree(1, n, 1); printf("Case %d:\n", ++cas); while(scanf("%s", s) != EOF) { if(s[0] == 'E') break; else if(s[0] == 'Q') { ans = 0; scanf("%d%d", &x, &y); search(x, y, 1); printf("%d\n", ans); } else if(s[0] == 'A') { scanf("%d%d", &x, &y); update(1, x, y); } else if(s[0] == 'S') { scanf("%d%d", &x, &y); update(1, x, -y); } } } return 0; }


然后是树状数组版


/* ID: sdj22251 PROG: lamps LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define MAX 2000000000 #define LOCA using namespace std; int a[50005], n, b[50005]; int lowbit(int x) { return x & -x; } void modify(int x) { for(int i = x; i <= n; i += lowbit(i)) a[i] += b[x]; } void modify2(int x, int y) { for(int i = x; i <= n; i += lowbit(i)) a[i] += y; } int sum(int x) { int ans = 0; for(int i = x; i >= 1; i -= lowbit(i)) ans += a[i]; return ans; } int main() { #ifdef LOCAL freopen("lamps.in","r",stdin); freopen("lamps.out","w",stdout); #endif int t, cas = 0, i, x, y; char s[15]; scanf("%d", &t); while(t--) { memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b)); scanf("%d", &n); for(i = 1; i <= n; i++) { scanf("%d", &b[i]); modify(i); } printf("Case %d:\n", ++cas); while(scanf("%s", s) != EOF) { if(s[0] == 'E') break; else if(s[0] == 'Q') { scanf("%d%d", &x, &y); printf("%d\n", sum(y) - sum(x - 1)); } else if(s[0] == 'A') { scanf("%d%d", &x, &y); modify2(x, y); } else if(s[0] == 'S') { scanf("%d%d", &x, &y); modify2(x, -y); } } } return 0; }


2.HDU 1754 I Hate It

同样是点更新,外加up操作即可,线段树所带信息是当前区间的最大值

/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 200005 #define INF 100000000 #define eps 1e-7 using namespace std; struct node { int left, right, mid; int l, mx; } tree[4 * MAXN]; int ans, num[MAXN]; void make_tree(int s, int e, int C) { tree[C].left = s; tree[C].right = e; tree[C].mid = ((s + e) >> 1); tree[C].l = tree[C].right - tree[C].left + 1; tree[C].mx = 0; if(s == e) {tree[C].mx = num[s]; return;} make_tree(s, tree[C].mid, (C << 1)); make_tree(tree[C].mid + 1, e, (C << 1) | 1); tree[C].mx = max(tree[C << 1].mx, tree[(C << 1) + 1].mx); } void update(int s, int v, int C) { if(tree[C].left == s && tree[C].right == s) { tree[C].mx = v; return; } if(s > tree[C].mid) { update(s, v, (C << 1) | 1); } else update(s, v, C << 1); tree[C].mx = max(tree[C << 1].mx, tree[(C << 1) + 1].mx); } void search(int s, int e, int C) { if(tree[C].left >= s && tree[C].right <= e) { if(tree[C].mx > ans) ans = tree[C].mx; return; } if(s > tree[C].mid) { search(s, e, (C << 1) | 1); } else if(e <= tree[C].mid) { search(s, e, C << 1); } else { search(s, tree[C].mid, C << 1); search(tree[C].mid + 1, e, (C << 1) | 1); } } int main() { #ifdef LOCAL freopen("d:/data.in","r",stdin); freopen("d:/data.out","w",stdout); #endif int n, m, i, x, y; char s[5]; while(scanf("%d%d", &n, &m) != EOF) { for(i = 1; i <= n; i++) scanf("%d", &num[i]); make_tree(1, n, 1); while(m--) { scanf("%s %d %d", s, &x, &y); if(s[0] == 'Q') { ans = 0; search(x, y, 1); printf("%d\n", ans); } else { update(x, y, 1); } } } return 0; }
3.HDU 1394Minimum Inversion Number

题意就是求逆序数,然后每次把当前序列的第一个数放到序列末尾求一次逆序数,总共n次,求其中最小的逆序数

其实求出最初的逆序数就行了,因为随后的逆序数能推导出来,总共n个数,0到n-1都有,比a[i]小的数是a[i] 个,大的数是n-1-a[i]个。

求逆序数时所用的方法是先查询再更新,查询的是已经存在的比自己大的数的个数,而线段树中所带的信息就是这一区间有几个数存在。

/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 50005 #define INF 100000000 #define eps 1e-7 #define L(x) x<<1 #define R(x) x<<1|1 using namespace std; struct node { int left, right, mid; int sum; }tree[4 * MAXN]; int tmp, a[MAXN]; void up(int C) { tree[C].sum = tree[L(C)].sum + tree[R(C)].sum; } void make_tree(int s, int e, int C) { tree[C].left = s; tree[C].right = e; tree[C].mid = (s + e) / 2; tree[C].sum = 0; if(s == e) return; make_tree(s, tree[C].mid, L(C)); make_tree(tree[C].mid + 1, e, R(C)); } void search(int s, int e, int C) { if(s <= tree[C].left && e >= tree[C].right) { tmp += tree[C].sum; return; } if(tree[C].mid >= e) search(s, e, L(C)); else if(tree[C].mid < s) search(s, e, R(C)); else { search(s, tree[C].mid, L(C)); search(tree[C].mid + 1, e, R(C)); } } void update(int p, int s, int e, int C) { if(s == e && s == p) {tree[C].sum++; return;} if(tree[C].mid >= p) update(p, s, tree[C].mid, L(C)); else update(p, tree[C].mid + 1, e, R(C)); up(C); } int main() { #ifdef LOCAL freopen("d:/data.in","r",stdin); freopen("d:/data.out","w",stdout); #endif int n, i; while(scanf("%d", &n) != EOF) { make_tree(0, n - 1, 1); int sum = 0; for(i = 0; i < n; i++) { scanf("%d", &a[i]); tmp = 0; search(a[i], n - 1, 1); sum += tmp; update(a[i], 0, n - 1, 1); } int ret = sum; for(i = 0; i < n; i++) { sum = sum + n - a[i] - a[i] - 1; ret = min(ret, sum); } printf("%d\n", ret); } return 0; }

4.HDU 2795 Billboard

题意是给出一个 高为h宽为w的木板,然后往上贴高度为1,宽度wi的纸条,每次帖优先找到最高的帖,高度相同的要尽量往左边贴。

其实把板子横过来的话就好理解了,横坐标是h,然后尽量往左边贴条子

数据范围貌似挺大的,其实也不大,因为n只有20万,也就是说线段树的范围1到20万够用了

然后线段树的附加信息是这个区间内每一段h的剩余空间的最大值,查询的时候最终查到叶子节点,减掉消耗的长度就行了。

/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 200005 #define INF 100000000 #define eps 1e-7 #define L(x) x<<1 #define R(x) x<<1|1 using namespace std; int h, w, n; struct node { int left, right, mid; int mx; }tree[4 * MAXN]; void make_tree(int s, int e, int C) { tree[C].left = s; tree[C].right = e; tree[C].mid = (s + e) / 2; tree[C].mx = w; if(s == e) return; make_tree(s, tree[C].mid, L(C)); make_tree(tree[C].mid + 1, e, R(C)); } int insert(int C, int v) { if(tree[C].mx < v) return -1; if(tree[C].left == tree[C].right) { tree[C].mx -= v; return tree[C].left; } int ans = -1; if(v <= tree[L(C)].mx) ans = insert(L(C), v); else if(v <= tree[R(C)].mx) ans = insert(R(C), v); tree[C].mx = max(tree[L(C)].mx, tree[R(C)].mx); return ans; } int main() { #ifdef LOCAL freopen("d:/data.in","r",stdin); freopen("d:/data.out","w",stdout); #endif int i, x; while(scanf("%d%d%d", &h, &w, &n) != EOF) { if(h > n) h = n; make_tree(1, h, 1); for(i = 0; i < n; i++) { scanf("%d", &x); printf("%d\n", insert(1, x)); } } return 0; }

然后做了这几个初级题目后,发现查询的时候,一种是需要自定义查询范围的,还有一种是不需要自定义查询范围, 直接从祖先开始查的,查询函数的参数的个数自然就不同了


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值