风力观测
发布时间: 2017年7月8日 22:05 最后更新: 2017年7月8日 23:29 时间限制: 1000ms 内存限制: 128M
小Y正在观测y地区的风力情况,他在一条直线上依此设定了 n 个观测点,并观测与直线垂直方向的风力值,风力有时是正向的也有时是反向的,规定正向时的风力值为正数,他发现每次风力值的变化都可以表示为观测点上一条线段 [L,R] 上的同时增强或者减弱。小Y希望能够实时统计这些观测点的数据,并且实时分析这些观测点在历史中到达的风力最大绝对值,但是他无法同时对大量的观测点进行分析, 更重要的是他记不住这些观测点过去的风力大小,于是他希望你来用计算机帮助他完成这个任务。
你简化了这个问题,将问题分为两种查询:
1.对观测点 [L,R] 上的风力正向增强 X 。( X 为负数表示正向减弱,即反向加强)
2.查询观测点 A 上的历史风力最大绝对值。
第一行有一个整数
T
表示数据组数。(
T<=10
)
接着有
T
组数据,每组数据第一行是整数
n
和
q
,表示观测点个数和查询次数。
第二行有
n
个数
a1,...,an
,表示每个观测点的风力初始值。
接着有
q
行,表示
q
次操作,格式为:
1 L R X:表示对
[L,R]
线段上的正向风力同时增强
x
。
2 A:表示查询
A
点的历史风力最大绝对值。
1<=n,q<=100000
。
1<=L,R,A<=n
−10000<=ai
,
X<=10000
对每次询问2,输出一个数字表示风力值并换行。
1 5 6 1 -1 2 3 -3 1 1 5 1 2 1 2 2 1 2 4 -5 2 2 2 3
2 1 5 3
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define rt 1,1,Q
#define ls o<<1
#define rs o<<1|1
#define mid (l+r>>1)
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, Q;
int a[N];
int mn[N * 4], mx[N * 4], flag[N * 4];
vector< pair<int,int> >prefix[N];
vector<int>q[N];
int ans[N];
void build(int o, int l, int r)
{
mn[o] = mx[o] = flag[o] = 0;
if (l == r)return;
build(lson);
build(rson);
}
void addit(int o, int val)
{
mn[o] += val;
mx[o] += val;
flag[o] += val;
}
void pushdown(int o)
{
if (flag[o])
{
addit(ls, flag[o]);
addit(rs, flag[o]);
flag[o] = 0;
}
}
void pushup(int o)
{
mn[o] = min(mn[ls], mn[rs]);
mx[o] = max(mx[ls], mx[rs]);
}
int L, R, V;
void change(int o, int l, int r)
{
if (L <= l && r <= R)
{
addit(o, V);
return;
}
pushdown(o);
if (L <= mid)change(lson);
if (R > mid)change(rson);
pushup(o);
}
int MN, MX;
void query(int o, int l, int r)
{
if (L <= l && r <= R)
{
gmin(MN, mn[o]);
gmax(MX, mx[o]);
return;
}
pushdown(o);
if (L <= mid)query(lson);
if (R > mid)query(rson);
}
int main()
{
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d%d", &n, &Q);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
prefix[i].clear();
q[i].clear();
}
for (int i = 1; i <= Q; ++i)
{
ans[i] = 2e9;
int op, l, r, val, x;
scanf("%d", &op);
if (op == 1)
{
scanf("%d%d%d", &l, &r, &val);
prefix[l].push_back({ i, val });
if (r < n) prefix[r + 1].push_back({ i, -val });
}
else
{
scanf("%d", &x);
q[x].push_back(i);
}
}
build(rt);
for (int i = 1; i <= n; ++i)
{
for (auto it : prefix[i])
{
L = it.first;
R = Q;
V = it.second;
change(rt);
}
for (auto it : q[i])
{
L = 1;
R = it;
MN = 2e9;
MX = -2e9;
query(rt);
ans[it] = max(abs(MN + a[i]), abs(MX + a[i]));
gmax(ans[it], abs(a[i]));
}
}
for (int i = 1; i <= Q; ++i)
if (ans[i] != 2e9)
printf("%d\n", ans[i]);
}
return 0;
}
/*
【题意】
有n(1e5)个数,a[1] ~ a[n], -1e4 <= a[] <= 1e4
我们有Q(1e5)次操作,
操作一,给定l, r, val(没说l <= r,但数据默认了,同时还有-1e4 <= val <= 1e4)
操作二,给定x,查询x的历史绝对值的最大值。
【分析】
这道题,如果我们把线段树的思维,仍然停留在第i个位置表示点i,那可能要坑很久。
其实维度除了区间,还有时间。
为何不思考一棵以时间为下标的线段树呢?
倘若以时间为下标,那这颗线段树全部都是服务于查询点的。
每次历史查询的时候,就相当于查询[0, 查询时刻]的最大值就好了。
然而,这样看起来似乎有很高的维护成本。
毕竟,整棵线段树,属于某个点,这也太奢侈了。
我们手里所能拥有的,只能是一棵线段树(很多时候是这样)
那怎么使得这棵线段树,只用于维护当前节点呢?
我们发现,我们的区间操作是具有连续性的。
于是,我们可以依次考虑使得这棵时间线段树是点1 ~ n的。
具体的维护方法,是使用前缀和标记实现。
我们每次做区间操作的时候——
在[l]位置,push进一个时间标记,一个权值val
在[r+1]位置,push进一个时间标记,一个权值-val
这样,我们从1~n扫描的时候,就可以使得关于某个点区间操作全部生效,生效在时间线段树上。
这样每次离线所有的查询,就可以AC啦!
比赛的时候因为提交卡题的时间问题没有AC,实在是太遗憾了!
不要忘记0时刻的也是历史时刻哦
*/