BZOJ4540 HNOI2016 序列
阿新 • • 發佈:2019-01-14
題目大意:給你一個長度為N的序列,每次詢問給定一個區間,詢問該區間內每個子區間的最小值的和。
很容易想到:對於一個元素可以求出Left和Right表示左端點在[Left, i],右端點在[i, Right]的區間最小值為這個元素(對於權值相同的可以任意指定相對大小)
題目樸素的做法,給你平面上N*N個點,每次詢問一個矩形內點的權值和,那麼每個元素實際上會將一個矩形內所有的點權值都改為該元素。那麼這個東西可以用樹套樹來做(?),但是考場上由於第三題思路不太清晰,花了3個小時才調完,於是這題這種做法就沒有時間寫了,只能60分收場。
上面那種做法實際上是考慮每個點對一些最小值的貢獻,換一種思路,直接考慮每個點對答案的貢獻。簡單展開一下,化成一個函式:F(x, y) = a * x + b * y + c * x * y +d
那麼對於詢問區間可能的左右端點分類討論,對於平面上每個點(就是一個詢問區間)維護F(x, y)的係數。那麼問題轉化成了給一個矩形內所有點加一個數,並且單點詢問。
既然是單點詢問+離線。那麼可以直接用掃描線+線段樹解決。
程式碼如下:
/* * @Author: duyixian * @Date: 2016-04-19 23:17:38 * @Last Modified by: 逸閒 * @Last Modified time: 2016-04-20 14:01:18 */ #include "cstdio" #include "cstdlib" #include "iostream" #include "algorithm" #include "cstring" #include "queue" #include "cmath" #include "set" using namespace std; #define INF 0x3F3F3F3F #define MAX_SIZE 100005 #define Eps #define Mod #define Get(x, a) (x ? x -> a : 0) #define Travel(x) for(typeof(x.begin()) it = x.begin(); it != x.end(); ++it) #define L(i) (i ? Mid + 1 : Left) #define R(i) (i ? Right : Mid) inline int Get_Int() { int Num = 0, Flag = 1; char ch; do { ch = getchar(); if(ch == '-') Flag = -Flag; } while(ch < '0' || ch > '9'); do { Num = Num * 10 + ch - '0'; ch = getchar(); } while(ch >= '0' && ch <= '9'); return Num * Flag; } class Data { public: long long a, b, c, d; Data(long long _a = 0, long long _b = 0, long long _c = 0, long long _d = 0) { a = _a; b = _b; c = _c; d = _d; } inline Data operator + (Data const &A) const { return Data(a + A.a, b + A.b, c + A.c, d + A.d); } inline Data operator - (Data const &A) const { return Data(a - A.a, b - A.b, c - A.c, d - A.d); } inline Data operator * (const long long A) const { return Data(a * A, b * A, c * A, d * A); } }; namespace Segment_Tree { Data Tag[MAX_SIZE * 4]; inline void Modify(int Now, int left, int right, Data Value, int Left, int Right) { if(left == Left && right == Right) { Tag[Now] = Tag[Now] + Value; return; } int Mid = Left + Right >> 1; if(left > Mid || right <= Mid) { int i = left > Mid; Modify(Now << 1 | i, left, right, Value, L(i), R(i)); return; } Modify(Now << 1, left, Mid, Value, L(0), R(0)); Modify(Now << 1 | 1, Mid + 1, right, Value, L(1), R(1)); } inline Data Query(int Now, int Position, int Left, int Right) { if(Left != Right) { int Mid = Left + Right >> 1, i = Position > Mid; return Tag[Now] + Query(Now << 1 | i, Position, L(i), R(i)); } return Tag[Now]; } } class Change { public: int x, Left, Right; Data Value; inline bool operator < (Change const &a) const { return x < a.x; } }Insert[MAX_SIZE * 8]; int N, Q, Total; int Sort[MAX_SIZE]; set<int> Set; long long Ans[MAX_SIZE], A[MAX_SIZE], Left[MAX_SIZE], Right[MAX_SIZE], X[MAX_SIZE], Y[MAX_SIZE]; inline bool cmp1(int a, int b) { return A[a] < A[b]; } inline bool cmp2(int a, int b) { return X[a] < X[b]; } inline void Add(int x1, int x2, int y1, int y2, Data Value) { if(x1 > x2) return; if(y1 > y2) return; ++Total; Insert[Total].x = x1; Insert[Total].Left = y1; Insert[Total].Right = y2; Insert[Total].Value = Value; ++Total; Insert[Total].x = x2 + 1; Insert[Total].Left = y1; Insert[Total].Right = y2; Insert[Total].Value = Insert[Total].Value - Value; } int main() { #ifndef ONLINE_JUDGE freopen("sequence.in", "r", stdin); freopen("sequence.out", "w", stdout); #endif cin >> N >> Q; for(int i = 1; i <= N; ++i) { Sort[i] = i; A[i] = Get_Int(); } sort(Sort + 1, Sort + N + 1, cmp1); Set.insert(0); Set.insert(N + 1); for(int i = 1; i <= N; ++i) { int Now = Sort[i]; set<int>::iterator it = Set.lower_bound(Now); Right[Now] = *it; --it; Left[Now] = *it; Set.insert(Now); } for(long long i = 1; i <= N; ++i) { Add(Left[i], i, i, Right[i], Data(i, i, -1, -i * i) * A[i]); Add(0, Left[i] - 1, i, Right[i], Data(0, i - Left[i], 0, i * Left[i] - i * i) * A[i]); Add(Left[i], i, Right[i] + 1, N + 1, Data(i - Right[i], 0, 0, i * Right[i] - i * i) * A[i]); Add(0, Left[i] - 1, Right[i] + 1, N + 1, Data(0, 0, 0, (i - Left[i]) * (Right[i] - i)) * A[i]); } sort(Insert + 1, Insert + Total + 1); for(int i = 1; i <= Q; ++i) { X[i] = Get_Int() - 1; Y[i] = Get_Int() + 1; Sort[i] = i; } sort(Sort + 1, Sort + Q + 1, cmp2); int Now1 = 1, Now; for(int i = 1; i <= Q; ++i) { Now = Sort[i]; while(Now1 <= Total && Insert[Now1].x <= X[Now]) { Segment_Tree::Modify(1, Insert[Now1].Left, Insert[Now1].Right, Insert[Now1].Value, 0, N + 1); ++Now1; } Data temp = Segment_Tree::Query(1, Y[Now], 0, N + 1); Ans[Now] = temp.c * X[Now] * Y[Now] + temp.a * X[Now] + temp.b * Y[Now] + temp.d; } for(int i = 1; i <= Q; ++i) printf("%lld\n", Ans[i]); fclose(stdin); fclose(stdout); return 0; }