[LOJ2019] 「AHOI / HNOI2017」影魔(離線+線段樹)

題意

  • 每次詢問一個區間的貢獻,一個區間的貢獻可以這樣計算,每存在一個點對(i,j)(i,j)滿足ki,kjk_i,k_j分別爲區間[i,j][i,j]的最大值和次大值就有p1p1貢獻,滿足(ji>1(j-i>1ki<maxx=i+1j1<kjk_i<\max_{x=i+1}^{j-1}<k_jkj<maxx=i+1j1<ki)k_j<\max_{x=i+1}^{j-1}<k_i)就有p2p2貢獻

這個題本來也想用莫隊做,但實際只能做到O(nnlogn)O(n\sqrt n\log n),會被卡,要找到一個O(nlogn)O(n\log n)的做法,我們可以先用單調棧計算出每個點左邊第一個比它大的數和每個點右邊第一個比它大的數,設爲Li,RiL_i,R_i,那麼對於每對Li,RiL_i,R_i一定會產生p1p1的貢獻,對於每個相鄰的i,i+1i,i+1也會產生p1p1的貢獻,然後對於每個數來說,LiL_i會與[i+1,Ri1][i+1,R_i-1]產生貢獻,RiR_i會與[Li+1,i1][L_i+1,i-1]產生貢獻,我們可以把所有詢問離線下來,依次加入每個位置的貢獻,類似於差分的思想加入l1l-1的時候減去[l,r][l,r]的貢獻,加入rr的時候加回來。對於這些貢獻p1p1的我們在加入RiR_i的時候更新LiL_i的貢獻,貢獻p2p2的我們在加入那個固定點的時候更新一段區間的貢獻,那麼用一個支持區間修改區間求和的線段樹就可以了,複雜度O(nlogn)O(n\log n)

#include <bits/stdc++.h>

#define ll long long
#define For(i, a, b) for (int i = a; i <= b; ++ i) 

using namespace std;

const int N = 2e5 + 10;

ll ans[N];

int a[N], L[N], R[N], Sta[N];
int n, m, p1, p2, top, cnt;

struct Segment_Tree {
#define mid ((l + r) >> 1)
#define ls (bh << 1)
#define rs (ls | 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r

	ll S[N << 2], lazy[N << 2]; 

	void modify(int bh, int l, int r, int z) {
		lazy[bh] += z, S[bh] += 1ll * (r - l + 1) * z;
	}

	void pushup(int bh) {
		S[bh] = S[ls] + S[rs];
	}

	void pushdown(int bh, int l, int r) {
		if (lazy[bh]) {
			modify(lson, lazy[bh]), modify(rson, lazy[bh]);
			lazy[bh] = 0;
		}
	}

	void update(int bh, int l, int r, int x, int y, int z) {
		if (x <= l && r <= y) modify(bh, l, r, z);
		else {
			pushdown(bh, l, r);
			if (x <= mid) update(lson, x, y, z);
			if (y > mid) update(rson, x, y, z);
			pushup(bh);
		}
	}

	ll query(int bh, int l, int r, int x, int y) {
		if (x <= l && r <= y) return S[bh];
		pushdown(bh, l, r);
		if (y <= mid) return query(lson, x, y);
		if (x > mid) return query(rson, x, y);
		return query(lson, x, y) + query(rson, x, y);
	}

}T;

struct Que {
	int opt, pos, l, r, val, id; 
	bool operator < (const Que &Tp) const {
		if (pos == Tp.pos) return opt < Tp.opt;
		return pos < Tp.pos;
	}
}Q[N << 3];

int main() {
#ifdef ylsakioi
	freopen("2019.in", "r", stdin);
	freopen("2019.out", "w", stdout);
#endif

	int x, y; 

	scanf("%d%d%d%d", &n, &m, &p1, &p2);
	For(i, 1, n) scanf("%d", &a[i]);
	For(i, 1, n) {
		for (; top && a[Sta[top]] < a[i]; -- top) 
			R[Sta[top]] = i; 
		L[i] = Sta[top], Sta[++ top] = i; 
	}
	while (top --) R[Sta[top + 1]] = n + 1; 
	For(i, 1, n) {
		if (i < n) Q[++ cnt] = (Que) {0, i + 1, i, i, p1, 0};
		if (1 <= L[i] && R[i] <= n) 
			Q[++ cnt] = (Que) {0, R[i], L[i], L[i], p1, 0};
		if (1 <= L[i] && R[i] > i + 1) 
			Q[++ cnt] = (Que) {0, L[i], i + 1, R[i] - 1, p2, 0};
		if (L[i] < i - 1 && R[i] <= n)
			Q[++ cnt] = (Que) {0, R[i], L[i] + 1, i - 1, p2, 0};
	}

	For(i, 1, m) {
		scanf("%d%d", &x, &y);
		Q[++ cnt] = (Que) {1, x - 1, x, y, -1, i};
		Q[++ cnt] = (Que) {1, y, x, y, 1, i};
	}

	sort(Q + 1, Q + cnt + 1);
	For(i, 1, cnt) {
		if (Q[i].opt) ans[Q[i].id] += T.query(1, 1, n, Q[i].l, Q[i].r) * Q[i].val;
		else T.update(1, 1, n, Q[i].l, Q[i].r, Q[i].val);
	}
	For(i, 1, m) printf("%lld\n", ans[i]);

	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章