風力觀測
發佈時間: 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時刻的也是歷史時刻哦
*/