Codeforces 1108E (Array and Segments) 线段树
题意:给你一个长度为n的序列和m组区间操作,每组区间操作可以把区间[l, r]中的数字都-1,请选择一些操作(可以都不选),使得序列的最大值和最小值的差值尽量的大。
思路:容易发现如果最大值和最小值都在某个操作区间里,那么这个操作没有意义,因为差值没变,所以我们可以想到暴力枚举每一个位置,假设这个位置的数是最小的,那么就把所有与他相关的区间操作都执行,执行完后找到当前序列的最大值更新答案即可。
E1数据很小,直接3重循环暴力枚举就可以过了,复杂度为O(n * n * m)。
对于E2,很明显如果每次操作完了从头到尾循环一遍找最大值很费时间,看标题也能想到最大值要用线段树来维护(2333),但是就算用线段树找最大值,复杂度还是O(n * m * logn)。
我们可以直观感受一下,对于每个枚举的位置,每次都暴力的把可以的区间操作加上,再暴力的还原这些操作,非常的浪费,所有我们可以从这里优化。
假设一个操作区间为[l, r],那么实际这个区间操作可以使[l, r]区间之中的值变得更小,在这个范围之外,这个操作是多余的。所以我们可以用差分的思想,我们记录一下这个区间对答案影响的开始位置和结束位置,扫描到区间开始时在线段树中加上该区间的影响,到区间末尾时消去该区间的影响。
因为每个操作只会添加和消去一次, 总复杂度为O(n * logn + m * logn), 其中的n * logn 是线段树的建树时间。
代码:
#include <bits/stdc++.h>
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define pii pair<int, int>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 100010;
int a[maxn]; struct node{
int mx, add;
int l, r;
}; vector<int> st[maxn], ed[maxn]; node tr[maxn * 4];
pii b[310];
vector<int> res;
void pushdown(int o) {
if(tr[o].add != 0) {
tr[ls(o)].add += tr[o].add;
tr[rs(o)].add += tr[o].add;
tr[ls(o)].mx += tr[o].add;
tr[rs(o)].mx += tr[o].add;
tr[o].add = 0;
}
} void maintain(int o) {
tr[o].mx = max(tr[ls(o)].mx,tr[rs(o)].mx);
}
void build(int o, int l, int r) {
if(l == r) {
tr[o].mx = a[l];
tr[o].add = 0;
tr[o].l = l;
tr[o].r = r;
return;
}
tr[o].l = l;
tr[o].r = r;
int mid = (l + r) >> 1;
build(ls(o), l, mid);
build(rs(o), mid + 1, r);
maintain(o);
} void update(int o, int l, int r, int ql, int qr, int add) {
if(l >= ql && r <= qr) {
tr[o].mx += add;
tr[o].add += add;
return;
}
pushdown(o);
int mid = (l + r) >> 1;
if(mid >= ql) update(ls(o), l, mid, ql, qr, add);
if(mid < qr) update(rs(o), mid + 1, r, ql, qr, add);
maintain(o);
} int query(int o, int l, int r, int ql ,int qr) {
if(l >= ql && r <= qr) {
return tr[o].mx;
}
int ans = -INF;
pushdown(o);
int mid = (l + r) >> 1;
if(ql <= mid) ans = max(ans, query(ls(o), l, mid, ql, qr));
if(qr > mid) ans = max(ans, query(rs(o), mid + 1, r, ql, qr));
return ans;
} int main() {
int n, m, ans = 0;
int mx = - INF, mi = INF;
// freopen("in.txt","r",stdin);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
mx = max(mx, a[i]);
mi = min(mi, a[i]);
}
ans = mx - mi;
build(1, 1, n);
for(int i = 1; i <= m; i++) {
scanf("%d%d",&b[i].first, &b[i].second);
st[b[i].first].push_back(b[i].second);
ed[b[i].second].push_back(b[i].first);
}
int pos = 0, tmp;
for(int i = 1; i <= n; i++) {
for(int j = 0; j < st[i].size(); j++) {
update(1, 1, n, i, st[i][j], -1);
}
tmp = query(1, 1, n, 1, n) - query(1, 1, n, i, i);
if(tmp > ans) {
ans = tmp;
pos = i;
}
for(int j = 0; j < ed[i].size(); j++) {
update(1, 1, n, ed[i][j], i, 1);
}
}
printf("%d\n", ans);
for(int i = 1; i <= m; i++) {
if(b[i].first <= pos && b[i].second >= pos)
res.push_back(i);
}
printf("%d\n",res.size());
for(int i = 0; i < res.size(); i++)
printf("%d ",res[i]);
}
Codeforces 1108E (Array and Segments) 线段树的更多相关文章
- Codeforces 610D Vika and Segments 线段树+离散化+扫描线
可以转变成上一题(hdu1542)的形式,把每条线段变成宽为1的矩形,求矩形面积并 要注意的就是转化为右下角的点需要x+1,y-1,画一条线就能看出来了 #include<bits/stdc++ ...
- codeforces Good bye 2016 E 线段树维护dp区间合并
codeforces Good bye 2016 E 线段树维护dp区间合并 题目大意:给你一个字符串,范围为‘0’~'9',定义一个ugly的串,即串中的子串不能有2016,但是一定要有2017,问 ...
- Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树扫描线
D. Vika and Segments 题目连接: http://www.codeforces.com/contest/610/problem/D Description Vika has an i ...
- Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树 矩阵面积并
D. Vika and Segments Vika has an infinite sheet of squared paper. Initially all squares are whit ...
- Codeforces Round #337 (Div. 2) D. Vika and Segments (线段树+扫描线+离散化)
题目链接:http://codeforces.com/contest/610/problem/D 就是给你宽度为1的n个线段,然你求总共有多少单位的长度. 相当于用线段树求面积并,只不过宽为1,注意y ...
- Codeforces 671C. Ultimate Weirdness of an Array(数论+线段树)
看见$a_i\leq 200000$和gcd,就大概知道是要枚举gcd也就是答案了... 因为答案是max,可以发现我们很容易算出<=i的答案,但是很难求出单个i的答案,所以我们可以运用差分的思 ...
- Codeforces 1108E2 Array and Segments (Hard version) 差分, 暴力
Codeforces 1108E2 E2. Array and Segments (Hard version) Description: The only difference between eas ...
- codeforces 22E XOR on Segment 线段树
题目链接: http://codeforces.com/problemset/problem/242/E E. XOR on Segment time limit per test 4 seconds ...
- Codeforces 588E. A Simple Task (线段树+计数排序思想)
题目链接:http://codeforces.com/contest/558/problem/E 题意:有一串字符串,有两个操作:1操作是将l到r的字符串升序排序,0操作是降序排序. 题解:建立26棵 ...
随机推荐
- SEM
SEM是Search Engine Marketing的缩写,中文意思是搜索引擎营销.SEM是一种新的网络营销形式.SEM所做的就是全面而有效的利用搜索引擎来进行网络营销和推广.SEM追求最高的性价比 ...
- LINUX下SYN FLOOD攻击及LINUX下SYN攻防简述
LINUX下SYN攻防战如下 (一)SYN攻击原理 SYN攻击属于DOS攻击的一种,它利用TCP协议缺陷,通过发送大量的半连接请求,耗费服务器CPU和内存资源.SYN攻击聊了能影响主机外,还可以危害路 ...
- kvm-控制台登陆配置
vm虚拟机能否像xen虚拟机一样通过virsh console 一样采用字符界面进行linux虚拟机控制台呢,答案是肯定的,默认情况下该命令是不起作用的,需要修改相关文件才能实现. 1. 配置方法如下 ...
- hdoj-3791-二叉搜索树(二叉搜索树模板题)
#include <cstring> #include <cstdio> #include <iostream> using namespace std; type ...
- (转)SPFA算法
原文地址:http://www.cnblogs.com/scau20110726/archive/2012/11/18/2776124.html 粗略讲讲SPFA算法的原理,SPFA算法是1994年西 ...
- SVN管理多个项目版本库 (windows,linux 通用)
SVN管理多个项目版本库: . 安装SVN服务器软件,路径: C:\Program Files\Subversion . 在D盘创建svn根目录D:\SVN-CM . 在D:\SVN-CM下创建SVN ...
- ecmall中的分页问题
<ecmall>Ecmall系统自带的分页功能 在Ecmall的二次开发中,分页是必不可少的.这个系统已经自带了分页功能,下面来看看如何使用这个分页. 下面是一个自定义的类,用于查看订单的 ...
- java多线程并发去调用一个类的静态方法安全性探讨
java多线程并发去调用一个类的静态方法安全性探讨 转自:http://blog.csdn.net/weibin_6388/article/details/50750035 这篇文章主要讲多线程对 ...
- 学习动态性能表(14)--v$parameter&v$system_parameter
学习动态性能表 第14篇--V$PARAMETER&V$SYSTEM_PARAMETER 2007.6.11 这两个视图列出的各参数项名称以及参数值.V$PARAMETER显示执行查询的se ...
- bzoj 5210 最大连通子块和——动态DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5210 似乎像bzoj4712一样,依然可以用别的方法做.但还是只写了动态DP. 当然是dp[ ...