2017.3.4 树状数组&线段树 考试小结

7 篇文章 0 订阅
4 篇文章 0 订阅

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.14
const 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 | 1
const 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 | 1
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);
}

把这个看懂了这道题应该就没什么问题了,再注意一下合并的时候维护Mlen,ML,MR就行了。

Code

#include<cstdio>
#include<algorithm>
using namespace std;

#define lson pos << 1
#define rson pos << 1 | 1
const 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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值