【“盛大遊戲杯”第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時刻的也是歷史時刻哦

*/


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