BZOJ4540 HNOI2016 序列
2016-04-20 21:27
288 查看
题目大意:给你一个长度为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)的系数。那么问题转化成了给一个矩形内所有点加一个数,并且单点询问。
既然是单点询问+离线。那么可以直接用扫描线+线段树解决。
代码如下:
很容易想到:对于一个元素可以求出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; }
相关文章推荐
- zepto.fullpage.js学习总结
- 常用查找算法的Java实现
- SGU239(搜索?)
- Windows7_X64搭建J2EE开发环境
- 今天遇到的小问题--&和&&的坑
- c++获取系统时间实例2
- iOS - 本地通知
- 详解可变参数列表
- 最小生成树---Prim算法和Kruskal算法
- POJ 2488 A Knight's Journey
- 数据库的事务四大特性
- Reverse Linked List II
- ASP.NET连接数据库并获取数据
- 清华EMBA课程系列思考之八 -- 营销管理
- 勤奋
- 最小生成树---Prim算法和Kruskal算法
- Linux make与cmake的区别
- python中字符串的ljust、rjust、center方法讲解
- 枚举类型的使用方法
- 为什么一款优秀的企业协同办公软件一定要有windows客户端