【“盛大游戏杯”第15届上海大学程序设计联赛 M】【线段树 时间维度线段树 前缀和标记 离线询问】风力观测 历史最大值

风力观测

发布时间: 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组数据,每组数据第一行是整数nq,表示观测点个数和查询次数。
第二行有n个数a1,...,an,表示每个观测点的风力初始值。
接着有q行,表示q次操作,格式为:
1 L R X:表示对[LR]线段上的正向风力同时增强x
2 A:表示查询A点的历史风力最大绝对值。
1<=n,q<=100000
1<=L,R,A<=n
10000<=aiX<=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时刻的也是历史时刻哦

*/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章