2017.3.4 树状数组&线段树 考试小结
来源:互联网 发布:linux pip apt get 编辑:程序博客网 时间:2024/06/18 11:24
2017.3.4考试小结
这个星期刚学了树状数组就考试
考的一脸懵逼
第一题就有个大坑,看都没看就往里跳
So 爆0
~
不说了,直接看题
task1
xth 砍树
时间限制: 1 Sec 内存限制: 128 MB
题目描述
在一个凉爽的夏夜,xth和:abbit来到花园里砍树。为啥米要砍树呢?是这样滴,
小菜儿的儿子窄森要出生了。Xth这个做伯伯的自然要做点什么。于是他决定带着
rabbit去收集一些木材,给窄森做一个婴儿车……( xth早就梦想着要天天打菜儿
他儿窄森的小PP,到时候在婴儿车里安装一个电子遥控手臂,轻轻一按,啪啪
啪……“乌卡卡一一”xth牙区恶滴笑了,“不要告诉rabbit,她会说我缺德的……”
xth如是说)。
花园里共有n棵树。为了花园的整体形象,rabbit要求xth只能在m个区域砍伐,我
们可以将这m个区域看成m个区间,树的间距相等,都是1,我们将每个区间设为
[x,y]。那么长度为k的区间中就有k棵树。树木的高度不等。现在xth想测量一下,
每个区间树木砍伐后所得的木材量是多少,而且每次测量后他都会砍下标号为(x+y)/2
的那棵作为纪念。以方便他安排人手。(同一个区间的树木可以重复砍伐,我们认
为被砍过的树木高度为0)
每棵树的木材量=树的高度*3.14(注意是3.14不是π)。
输入
第一行,一个整数n。
第二行,共n个整数,表示每棵树的高度。
第三行,一个整数m,表示共m个区间。
以下m行,每个区间[x, y]的左右端点x, y。
输出
共m行,每行一个数,表示每个区间的木材量。
结果精确到小数点后两位。
样例输入
5
1 2 3 4 5
2
1 4
2 4
样例输出
31.40
21.98
提示
【数据规模】
对于30%的数据,有n ≤ 5000,m ≤ 5000;
对于100%的数据,有n ≤ 200000,m ≤ 200000;
【样例解释】
第一次砍[1,4] ,森林变为:1 0 3 4 5
题解
其实就是一道树状数组水题
注意一下可以砍重复区域就行了(考试的时候没看到QAQ 愉快爆0)
Code
#include<cstdio>#define lowbit(x) (x & (-x))#define P 3.14const int MAXN = 200005;int arr[MAXN], temp[MAXN], n, m;inline void Read(int &Ret){ char ch; int flg = 1; while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') flg = -1; Ret = ch - '0'; while(ch = getchar(), ch >= '0' && ch <= '9') Ret = Ret * 10 + ch - '0'; ungetc(ch, stdin); Ret *= flg;}inline int getsum(int pos){ int sum = 0; while(pos){ sum += arr[pos]; pos -= lowbit(pos); } return sum;}inline void update(int pos, int v){ while(pos <= n){ arr[pos] += v; pos += lowbit(pos); }}int main(){ Read(n); for(int i = 1; i <= n; ++ i){ Read(temp[i]); update(i, temp[i]); } Read(m); int x, y, mid, ans; while(m --){ Read(x); Read(y); mid = (x + y) >> 1; ans = getsum(y) - getsum(x - 1); printf("%.2lf\n",ans * P); if(temp[mid]){ update(mid, -temp[mid]); temp[mid] = 0; } } return 0;}
task2
编舞
时间限制: 5 Sec 内存限制: 256 MB
题目描述
Mirko和Slavko的踢踏舞跳的很好,现在他们想自己来编舞。踢踏舞的动作可以描述为两个字母的序列,‘L’和‘R’。‘L’表示你用左脚踢地板,‘R’表示你用右脚踢地板。Mriko现在定义编舞值为在序列中,不包含2个连续的L或R的最长的子序列。
一个舞蹈在最终完成前,需要经过多次修改。每一次修改Slavko都想知道当前的编舞值。每一次修改是把1个L变成R,或反过来把1个R变成L。
最开始的舞蹈序列只包括L.
输入
输入:
第一行包含2个整数,这个序列的长度N(1<=N<=200000)和修改的次数Q(1<=Q<=200000)。
接下来的Q行,每一行一个整数,描述了Mirko和Slavko修改的位置。
输出
输出:
输出包含Q个整数,每行一个,每一次修改后的编舞值。
样例输入
6 2
2
4
样例输出
3
5
提示
第一个样例解释:LLLLLL——LRLLLL——LRLRLL.
题解
思路:先递归到最底层进行修改,然后一层层返回的时候经行合并。
合并的时候维护3个值ML, MR, Mlen;
ML 表示当前区间从左延伸的最长合法01串长度
MR 表示当前区间从右延伸的最长合法01串长度
Mlen 表示当前区间总的最长合法01串长度(包括从左,从右,和中间)
不想多说,直接看代码吧
Code
#include<cstdio>#include<algorithm>using namespace std;#define lson pos << 1#define rson pos << 1 | 1const int MAXN = 200005;struct node{ int l, r; int Mlen, ML, MR; //Mlen ->tot //ML ->from left //MR ->from right bool L, R; // 0->L 1->R node(){} int len(){return r - l + 1;}}tree[MAXN * 4];inline void Read(int &Ret){ char ch; int flg = 1; while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') flg = -1; Ret = ch - '0'; while(ch = getchar(), ch >= '0' && ch <= '9') Ret = Ret * 10 + ch - '0'; ungetc(ch, stdin); Ret *= flg;}void build(int pos, int l, int r){ tree[pos].l = l; tree[pos].r = r; tree[pos].Mlen = tree[pos].ML = tree[pos].MR = 1; if(l == r) return; int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r);}inline void pushup(int pos){ tree[pos].Mlen = max(tree[lson].Mlen, tree[rson].Mlen); if(tree[lson].R != tree[rson].L) tree[pos].Mlen = max(tree[pos].Mlen, tree[lson].MR + tree[rson].ML); tree[pos].L = tree[lson].L; tree[pos].R = tree[rson].R; tree[pos].ML = tree[lson].ML; tree[pos].MR = tree[rson].MR; if(tree[lson].Mlen == tree[lson].len() && tree[lson].R != tree[rson].L) tree[pos].ML += tree[rson].ML; if(tree[rson].Mlen == tree[rson].len() && tree[lson].R != tree[rson].L) tree[pos].MR += tree[lson].MR;}void Insert(int pos, int x){ if(tree[pos].len() == 1){ tree[pos].L ^= 1; tree[pos].R ^= 1; return; } int mid = (tree[pos].l + tree[pos].r) >> 1; if(x <= mid) Insert(lson, x); else Insert(rson, x); pushup(pos);}int main(){ int n, m, x; Read(n); Read(m); build(1, 1, n); while(m --){ Read(x); Insert(1, x); printf("%d\n",tree[1].Mlen); } return 0;}
task3
问题 C(3206): 座位
时间限制: 1 Sec 内存限制: 128 MB
题目描述
一个餐馆中有n个座位,这些座位排成一条直线,从左到右编号为1,2,……n。这天有m个事件发生。事件有两种:一种是有新的订单,需要p个连续的座位。如果可以满足,就找到编号最小的一组给他们坐,如果不能满足,则要拒绝这个订单;一种是区间[a,b]的人全部离开。请把餐馆的老板统计有多少个订单不能满足。
输入
第一行有两个整数,N,M。(1<=N<=500000,1<=m<=300000)
接下来从第2行到第M行,表示有M个事件。要么是“A p”的形式,表示有订单,需要p个连续座位,要么是”L a b”的形式,表示区间[a,b]的人离开。
输出
需要拒绝的订单的数量。
样例输入
10 4
A 6
L 2 4
A 5
A 2
样例输出
1
题解
思路:解题方法其实跟第二题差不多,还是线段树,每个节点维护Mlen,ML, MR。
ML 表示当前区间从左延伸的最长连续空座位
MR 表示当前区间从右延伸的最长连续空座位
Mlen 表示当前区间总的最长最长连续空座位
(包括从左,从右,和中间)
覆盖和清除都差不多直接Copy过来改一下就行了~
重点说一下找编号最小的问题(考试的时候就是卡在这里了。。。)
因为我们维护了Mlen所以每次询问直接与根节点(就算是最大的哪个区间(1~n))的Mlen比较就行了,这样就是O(1)的查询。
如果需要的座位>根节点的Mlen,就直接 ans ++
否则就从根结点开始往下找
找的时候注意优先级,按左,中,右的顺序来找就行了(就是先看左儿子的ML,不行的再看左儿子的M,还不行就看左儿子的MR+右儿子的ML做后还不行才在右儿子里面去找)
看下代码你就懂了
#define lson pos << 1#define rson pos << 1 | 1int Find(int pos, int ned){ if(tree[lson].ML >= ned) return tree[pos].l; else if(tree[lson].Mlen >= ned) return Find(lson, ned); else if(tree[lson].MR + tree[rson].ML >= ned) return tree[lson].r - tree[lson].MR + 1; else return Find(rson, ned);}
把这个看懂了这道题应该就没什么问题了,再注意一下合并的时候维护Mlen,ML,MR就行了。
Code
#include<cstdio>#include<algorithm>using namespace std;#define lson pos << 1#define rson pos << 1 | 1const int MAXN = 500000;struct node{ int l, r, flg, len; // flg是懒标记, 0->无人 1->有人 -1->已传过 int Mlen, ML, MR;}tree[MAXN * 4];inline void Read(int &Ret){ char ch; int flg = 1; while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') flg = -1; Ret = ch - '0'; while(ch = getchar(), ch >= '0' && ch <= '9') Ret = Ret * 10 + ch - '0'; ungetc(ch, stdin); Ret *= flg;}void build(int pos, int l, int r){ tree[pos].l = l; tree[pos].r = r; tree[pos].len = tree[pos].ML = tree[pos].MR = tree[pos].Mlen = r - l + 1; tree[pos].flg = -1; if(l == r) return; int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r);}inline void pushdown(int pos){ if(tree[pos].flg == -1) return; tree[lson].flg = tree[rson].flg = tree[pos].flg; if(tree[pos].flg){ tree[lson].Mlen = tree[lson].ML = tree[lson].MR = 0; tree[rson].Mlen = tree[rson].ML = tree[rson].MR = 0; } else{ tree[lson].Mlen = tree[lson].ML = tree[lson].MR = tree[lson].len; tree[rson].Mlen = tree[rson].ML = tree[rson].MR = tree[rson].len; } tree[pos].flg = -1;}inline void pushup(int pos){ tree[pos].Mlen = max(max(tree[lson].Mlen, tree[rson].Mlen), tree[lson].MR + tree[rson].ML); tree[pos].ML = tree[lson].ML; tree[pos].MR = tree[rson].MR; if(tree[lson].Mlen == tree[lson].len) tree[pos].ML += tree[rson].ML; if(tree[rson].Mlen == tree[rson].len) tree[pos].MR += tree[lson].MR;}void Insert(int pos, int l, int r, bool flg){ if(r < tree[pos].l || tree[pos].r < l) return; if(l <= tree[pos].l && tree[pos].r <= r){ tree[pos].flg = flg; if(flg) tree[pos].Mlen = tree[pos].ML = tree[pos].MR = 0; else tree[pos].Mlen = tree[pos].ML = tree[pos].MR = tree[pos].len; return; } pushdown(pos); Insert(lson, l, r, flg); Insert(rson, l, r, flg); pushup(pos);}int Find(int pos, int ned){ if(tree[lson].ML >= ned) return tree[pos].l; else if(tree[lson].Mlen >= ned) return Find(lson, ned); else if(tree[lson].MR + tree[rson].ML >= ned) return tree[lson].r - tree[lson].MR + 1; else return Find(rson, ned);}int main(){ int n, m, a, b, ans = 0; char id[3]; Read(n); Read(m); build(1, 1, n); while(m --){ scanf("%s",id); Read(a); if(id[0] == 'A'){ if(a > tree[1].Mlen) ans ++; else{ b = Find(1, a); Insert(1, b, b + a - 1, 1); } } else{ Read(b); Insert(1, a, b, 0); } } printf("%d\n",ans); return 0;}
- 2017.3.4 树状数组&线段树 考试小结
- 线段树,树状数组
- 线段树,树状数组
- 线段树,树状数组
- 线段树,树状数组
- 树状数组-线段树
- 线段树 && 树状数组
- 线段树,树状数组
- 线段树&&树状数组
- 线段树+树状数组
- 树状数组和线段树
- 线段树and树状数组
- 线段树+树状数组整理
- 线段树、树状数组问题
- 线段树与树状数组
- poj3321 树状数组 线段树
- hdu1166树状数组||线段树。。
- 树状数组&线段树&RMQ
- JZOJ 3631. 【汕头市选2014】三角(triangle)
- PL/SQL developer基础语法学习(一)
- Java 绘图技术
- C++ string的操作函数
- Sicily Huffman coding | 优先队列
- 2017.3.4 树状数组&线段树 考试小结
- Lineage OS Build for armani(转过来留作参考)
- Java解压带密码的Rar压缩文件
- Android 官方推荐 : DialogFragment 创建对话框
- SD高清图片处理--内存疯长解决方法
- Window下VS打造dll 详细 简单步骤
- Linux简介
- 00003 不思议迷宫.0009.7:一键采矿(钻石、金蛋等)
- Week Training: 240 Search a 2D Matrix II