【HNOI2017】影魔

题面

题解

对于两个位置(l, r),如果它们分别是区间([l, r])的最大值,那么可以产生(p1)的贡献,

否则如果它们中有一个是最大值,那么可以产生(p2)的贡献。

所以对于当前位置(i),假设左右两边第一个比它大的是(l, r),那么([l, r])可以产生p1的贡献,([l + 1 sim i - 1, r])([l, i + 1sim r - 1])会产生(p2)的贡献。

于是加完(R[i])后,对(L[i])产生(p1)的贡献

加完位置(L[i])后,对((i, R[i]))产生(p2)的贡献

加完位置(R[i])后,对((L[i], i))产生(p2)的贡献

那么对于一个询问(L, R),只要考虑它范围内的贡献,于是也拆成三个部分

加入完(L - 1)时,要忽略前面(L, R)位置产生的所有贡献

加入完(R)时,要考虑所有的(L, R)位置产生的贡献

还有每一组的([i, i + 1])产生的贡献(p1)

这样的话,用扫描线+区间加法+区间求和即可。

用单调栈和树状数组就行了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define clear(x, y) memset(x, y, sizeof(x))

namespace IO
{
	const int BUFSIZE = 1 << 20;
	char ibuf[BUFSIZE], *is = ibuf, *it = ibuf;
	inline char getchar() { if (is == it) it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin); return *is++; }
}

inline int read()
{
	int data = 0, w = 1;
	char ch = IO::getchar();
	while(ch != '-' && (ch < '0' || ch > '9')) ch = IO::getchar();
	if(ch == '-') w = -1, ch = IO::getchar();
	while(ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = IO::getchar();
	return data * w;
}

const int maxn(2e5 + 10);
int n, m, p1, p2, stk[maxn], top, cnt, tot;
int L[maxn], R[maxn], a[maxn];
long long c1[maxn], c2[maxn], ans[maxn];

struct upd { int x, l, r, v; } p[maxn << 2];
struct qry { int x, l, r, v, id; } q[maxn << 2];
inline int operator < (const upd &a, const upd &b) { return a.x < b.x; }
inline int operator < (const qry &a, const qry &b) { return a.x < b.x; }

void add(int x, int w)
{
	for(RG int i = x; i <= n; i += i & -i)
		c1[i] += w, c2[i] += x * w;
}

long long query(int x)
{
	long long ans = 0;
	for(RG int i = x; i; i -= i & -i)
		ans += (x + 1) * c1[i] - c2[i];
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
	file(cpp);
#endif
	n = read(), m = read(), p1 = read(), p2 = read();
	for(RG int i = 1; i <= n; i++) a[i] = read();
	stk[top = 0] = 0;
	for(RG int i = 1; i <= n; i++)
	{
		while(top && a[stk[top]] < a[i]) --top;
		L[i] = stk[top], stk[++top] = i;
	}

	stk[top = 0] = n + 1;
	for(RG int i = n; i; i--)
	{
		while(top && a[stk[top]] < a[i]) --top;
		R[i] = stk[top], stk[++top] = i;
	}

	for(RG int i = 1; i <= m; i++)
	{
		int l = read(), r = read(); ans[i] = (r - l) * p1;
		q[++cnt] = (qry) {r, l, r, 1, i};
		q[++cnt] = (qry) {l - 1, l, r, -1, i};
	}
	std::sort(q + 1, q + cnt + 1);
	for(RG int i = 1; i <= n; i++)
	{
		if(L[i] && R[i] < n + 1) p[++tot] = (upd) {R[i], L[i], L[i], p1};
		if(L[i] && R[i] > i + 1) p[++tot] = (upd) {L[i], i + 1, R[i] - 1, p2};
		if(L[i] + 1 < i && R[i] < n + 1)
			p[++tot] = (upd) {R[i], L[i] + 1, i - 1, p2};
	}
	std::sort(p + 1, p + tot + 1);
	for(RG int i = 1, j = 1; i <= cnt; i++)
	{
		while(j <= tot && p[j].x <= q[i].x)
			add(p[j].l, p[j].v), add(p[j].r + 1, -p[j].v), ++j;
		ans[q[i].id] += q[i].v * (query(q[i].r) - query(q[i].l - 1));
	}
	for(RG int i = 1; i <= m; i++) printf("%lld
", ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/cj-xxz/p/10435269.html