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;}
0 0
原创粉丝点击