BZOJ4540 HNOI2016 序列

版权声明:都是TATQAQ2333大爷教我的 https://blog.csdn.net/u012076197/article/details/51204217

题目大意:给你一个长度为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;
}


猜你喜欢

转载自blog.csdn.net/u012076197/article/details/51204217