您的位置:首页 > 其它

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)的系数。那么问题转化成了给一个矩形内所有点加一个数,并且单点询问。

既然是单点询问+离线。那么可以直接用扫描线+线段树解决。

代码如下:

/*
* @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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: