[题解] bzoj 4826 hnoi 2017 影魔(线段树 + 扫描线)

- 传送门 -

 http://www.lydsy.com/JudgeOnline/problem.php?id=4826

# 4826: [Hnoi2017]影魔

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 539  Solved: 293
[Submit][Status][Discuss]

Description

影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。千百年来,他收集了各式各样的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。每一个灵魂,都有着自己的战斗力,而影魔,靠这些战斗力提升自己的攻击。奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。

第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i,j(i< j)来说,若不存在 k[s](i < s < j)大于 k[i]或者 k[j],则会为影魔提供 p1 的攻击力(可理解为:当 j=i+1 时,因为不存在满足 i< s < j 的 s,从而k[s]不存在,这时提供 p1 的攻击力;当 j>i+1 时,若max{k[s]|i < s < j}<=min{k[i],k[j]} ,则提供p1的攻击力); 另一种情况 ,令c为k[i+1],k[i+2],k[i+3]......k[j-1]的最大值,若 c 满足:k[i] < c < k[j],或者 k[j] < c < k[i],则会为影魔提供 p2 的攻击力,当这样的 c 不存在时,自然不会提供这 p2 的攻击力;其他情况的点对,均不会为影魔提供攻击力。影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任意一段区间[a,b],1 <= a < b <= n, 位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑 所有满足a <= i < j <= b 的灵魂对 i,j 提供的攻击力之和。顺带一提,灵魂的战斗力组成一个 1 到 n 的排列:k 1 ,k[2],...,k[n]。

Input

第一行 n,m,p1,p2

第二行 n 个数:k 1 ,k[2],...,k[n]

接下来 m 行,每行两个数 a,b,表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。

1 <= n,m <= 200000;1 <= p1,p2 <= 1000

Output

共输出 m 行,每行一个答案,依次对应 m 个询问。

Sample Input

10 5 2 3
7 9 5 1 3 10 6 8 2 4
1 7
1 9
1 3
5 9
1 5

Sample Output

30
39
4
13
16
 

- 题意 -

 对于一个序列A, 若点对(i, j)满足A[i], A[j] 均大于(i, j)内所有数, 该点对有一个贡献值p1, 若其中有一个满足该条件, 则该点对有一个贡献值p2, 问对于给定区间[a, b], 其中所有点对的总贡献值是多少.
 

- 思路 -

 设 l[i] 为 i 往左数第一个大于 A[i] 的数的位置, r[i] 为 i 往右数第一个大于 A[i] 的数的位置.
 可以得到:
  1. 点对(l[i] , r[i])有 p1 的贡献.(注意这里没有考虑(i, i+1)造成的 p1 贡献, 要在最后加上)
  2. 点对(l[i], j) (i < j < r[i]) 有 p2 的贡献.
  3. 点对(j, r[i]) (l[i] < j < i) 有 p2 的贡献.
 我们试着把点对转移到平面上去.
 点对的左端点指横坐标, 右端点指纵坐标, 这样就将以上有贡献的点对转化成了平面上有权值的点, 第 2 组点对会形成若干条与 y 轴平行的线, 第 3 组同理, 与 x 轴平行.
 对于询问[a , b], 我们要找以(a , b), (n, 1)为对角线的矩形内(包括边界)的点(线)的权值和.(a 表示点对的左端, 故可以增大, b 可以减小, 但是注意当纵坐标大于横坐标时实际上是没有点存在的).
 这就可以用扫描线了, 扫一遍横线再扫一遍竖线, 第一组点对(也就是平面上的点)试做线段随便加到 2 或 3 组去.
 不同于平常的扫描线, (以 x 轴平行的线为例, 我们将同一高度的线存在一起, 然后再将同一高度的询问(矩形的高)也存起来, 这样就可以再处理完一个高度的线之后马上处理该高度的询问), 并且注意扫的方向.
 
 细节见代码.
 
 PS:
 树状数组快很多, 这里用到了树状数组的区间修改区间查询, 代码贴在最后.
 

- 代码 -


#include<cstdio>
#include<algorithm>
#include<vector>
#define s second
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;

typedef long long ll;
const int M = 2e5 + 5;

int K[M], L[M], R[M];
int QL[M], QR[M];
ll ADD[M << 2], SUM[M << 2];
ll ANS[M];
int n, m, p1, p2;
int a, b, x, y;
int cntx, cnty;

vector<int> QX[M], QY[M], XX[M], YY[M];
pair<int, int> pai, PA[M];

struct edge {
	int a, b1, b2, c;
}WX[M<<1], WY[M];

template <typename T> void read(T &x) {
	x = 0; int f = 1; char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x*10 + ch - '0'; ch = getchar(); }
	x *= f;
}

void init() {
	int tmp = 1;
	L[1] = 0;
	PA[1] = make_pair(K[1], 1);
	for (int i = 2; i <= n; ++i) {
		pai = make_pair(K[i], i);
		while (pai > PA[tmp] && tmp) tmp--;
		if (!tmp) L[i] = 0;
		else L[i] = PA[tmp].s;
		PA[++tmp] = pai;
	}
	tmp = 1;
	R[n] = n + 1;
	PA[1] = make_pair(K[n], n);
	for (int i = n - 1; i >= 1; --i) {
		pai = make_pair(K[i], i);
		while (pai > PA[tmp] && tmp) tmp--;
		if (!tmp) R[i] = n + 1;
		else R[i] = PA[tmp].s;
		PA[++tmp] = pai;
	} //先用单调栈找l[i], r[i]
}

void pd(int rt, int l, int r) {
	if (ADD[rt]) {
		int tmp = ADD[rt], mid = l + r >> 1;
		ADD[ls] += tmp;
		ADD[rs] += tmp;
		SUM[ls] += tmp * (mid - l + 1);
		SUM[rs] += tmp * (r - mid);
		ADD[rt] = 0;
	}
}//标记下传

void add(int rt, int l, int r, int x, int y, int c) {
	if (x <= l && r <= y) {
		ADD[rt] += c;
		SUM[rt] += c * (r - l + 1);
		return;
	}
	pd(rt, l, r);
	int mid = l + r >> 1;
	if (x <= mid) add(ls, l, mid, x, y, c);
	if (mid < y) add(rs, mid + 1, r, x, y, c);
	SUM[rt] = SUM[ls] + SUM[rs];
}

ll query(int rt, int l, int r, int x, int y) {
	if (x <= l && r <= y)
		return SUM[rt];
	pd(rt, l, r);
	ll ans = 0;
	int mid = l + r >> 1;
	if (x <= mid) ans += query(ls, l, mid, x, y);
	if (mid < y) ans += query(rs, mid + 1, r, x, y);
	return ans;
}

void rebuild(int rt, int l, int r) {
	ADD[rt] = SUM[rt] = 0;
	if (l == r) return;
	int mid = l + r >> 1;
	rebuild(ls, l, mid);
	rebuild(rs, mid + 1, r);
}

int main() {
	read(n), read(m), read(p1), read(p2);
	for (int i = 1; i <= n; ++i)
		read(K[i]);
	init();
	for (int i = 1; i <= m; ++i) {
		read(QL[i]), read(QR[i]);
		QX[QR[i]].push_back(i);
		QY[QL[i]].push_back(i);
	}//记录询问, 对于每个询问要扫一遍横线, 扫一遍竖线, 故要在横轴数轴上各标记一次
	for (int i = 1; i <= n; ++i) {
		if (L[i] > 0 && R[i] < n + 1) {
			WX[++cntx].a = R[i], WX[cntx].c = p1;
			WX[cntx].b1 = WX[cntx].b2 = L[i];
			XX[R[i]].push_back(cntx);
		}//记录贡献为 p1 的点, 我将它归到了横线中
		if (L[i] > 0 && i + 1 < R[i]) {
			WY[++cnty].a = L[i], WY[cnty].c = p2;
			WY[cnty].b1 = i + 1, WY[cnty].b2 = R[i] - 1;
			YY[L[i]].push_back(cnty);
		}//记录竖线
		if (R[i] < n + 1 && L[i] < i - 1) {
			WX[++cntx].a = R[i], WX[cntx].c = p2;
			WX[cntx].b1 = L[i] + 1, WX[cntx].b2 = i - 1;
			XX[R[i]].push_back(cntx);
		}//记录横线
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j < XX[i].size(); ++j) {
			int tmp = XX[i][j];
			add(1, 1, n, WX[tmp].b1, WX[tmp].b2, WX[tmp].c);
		}//添加线
		for (int j = 0; j < QX[i].size(); ++j) {
			ANS[QX[i][j]] += query(1, 1, n, QL[QX[i][j]], n);
		}//同一高度上的询问
	}//横扫
	rebuild(1, 1, n);
	for (int i = n; i >= 1; --i) {
		for (int j = 0; j < YY[i].size(); ++j) {
			int tmp = YY[i][j];
			add(1, 1, n, WY[tmp].b1, WY[tmp].b2, WY[tmp].c);
		}
		for (int j = 0; j < QY[i].size(); ++j) {
			ANS[QY[i][j]] += query(1, 1, n, 1, QR[QY[i][j]]);
		}
	}//竖扫
	for (int i = 1; i <= m; ++i)
		printf("%lld
", ANS[i] + (QR[i] - QL[i]) * p1);//考虑[i, i+1]造成的 p1 贡献
	return 0;
}

 
 树状数组版:
 


#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define s second
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;

typedef long long ll;
const int M = 2e5 + 5;

int K[M], L[M], R[M];
int QL[M], QR[M];
ll B[M], C[M];
ll ANS[M];
int n, m, p1, p2;
int a, b, x, y;
int cntx, cnty;

vector<int> QX[M], QY[M], XX[M], YY[M];
pair<int, int> pai, PA[M];

struct edge {
	int a, b1, b2, c;
}WX[M<<1], WY[M];

template <typename T> void read(T &x) {
	x = 0; int f = 1; char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x*10 + ch - '0'; ch = getchar(); }
	x *= f;
}

void init() {
	int tmp = 1;
	L[1] = 0;
	PA[1] = make_pair(K[1], 1);
	for (int i = 2; i <= n; ++i) {
		pai = make_pair(K[i], i);
		while (pai > PA[tmp] && tmp) tmp--;
		if (!tmp) L[i] = 0;
		else L[i] = PA[tmp].s;
		PA[++tmp] = pai;
	}
	tmp = 1;
	R[n] = n + 1;
	PA[1] = make_pair(K[n], n);
	for (int i = n - 1; i >= 1; --i) {
		pai = make_pair(K[i], i);
		while (pai > PA[tmp] && tmp) tmp--;
		if (!tmp) R[i] = n + 1;
		else R[i] = PA[tmp].s;
		PA[++tmp] = pai;
	}
}

int lowbit(int x) { return x&(-x); }

void add(int rt, int c) {
	for (int i = rt; i <= n; i += lowbit(i)) {
		B[i] += c;
		C[i] += c * rt;
	}
}

ll query(int rt, int k) {
	ll ans = 0;
	for (int i = rt; i > 0; i -= lowbit(i)) {
		ans += k * B[i];
		ans -= C[i];
	}
	return ans;
}

int main() {
	read(n), read(m), read(p1), read(p2);
	for (int i = 1; i <= n; ++i)
		read(K[i]);
	init();
	for (int i = 1; i <= m; ++i) {
		read(QL[i]), read(QR[i]);
		QX[QR[i]].push_back(i);
		QY[QL[i]].push_back(i);
	}
	for (int i = 1; i <= n; ++i) {
		if (L[i] > 0 && R[i] < n + 1) {
			WX[++cntx].a = R[i], WX[cntx].c = p1;
			WX[cntx].b1 = WX[cntx].b2 = L[i];
			XX[R[i]].push_back(cntx);
		}
		if (L[i] > 0 && i + 1 < R[i]) {
			WY[++cnty].a = L[i], WY[cnty].c = p2;
			WY[cnty].b1 = i + 1, WY[cnty].b2 = R[i] - 1;
			YY[L[i]].push_back(cnty);
		}
		if (R[i] < n + 1 && L[i] < i - 1) {
			WX[++cntx].a = R[i], WX[cntx].c = p2;
			WX[cntx].b1 = L[i] + 1, WX[cntx].b2 = i - 1;
			XX[R[i]].push_back(cntx);
		}
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j < XX[i].size(); ++j) {
			int tmp = XX[i][j];
			add(WX[tmp].b1, WX[tmp].c);
			add(WX[tmp].b2 + 1, -WX[tmp].c);
		}
		for (int j = 0; j < QX[i].size(); ++j) {
			int tmp = QX[i][j];
			ANS[tmp] += query(n, n + 1);
			ANS[tmp] -= query(QL[tmp] - 1, QL[tmp]);
		}
	}
	memset(B, 0, sizeof B);
	memset(C, 0, sizeof C);
	for (int i = n; i >= 1; --i) {
		for (int j = 0; j < YY[i].size(); ++j) {
			int tmp = YY[i][j];
			add(WY[tmp].b1, WY[tmp].c);
			add(WY[tmp].b2 + 1, -WY[tmp].c);
		}
		for (int j = 0; j < QY[i].size(); ++j) {
			int tmp = QY[i][j];
			ANS[tmp] += query(QR[tmp], QR[tmp] + 1);
			ANS[tmp] -= B[1];
		}
	}
	for (int i = 1; i <= m; ++i)
		printf("%lld
", ANS[i] + (QR[i] - QL[i]) * p1);
	return 0;
}
原文地址:https://www.cnblogs.com/Anding-16/p/7327828.html