[LOJ2019] 「AHOI / HNOI2017」影魔(离线+线段树)

题意

  • 每次询问一个区间的贡献,一个区间的贡献可以这样计算,每存在一个点对 ( i , j ) (i,j) (i,j)满足 k i , k j k_i,k_j ki,kj分别为区间 [ i , j ] [i,j] [i,j]的最大值和次大值就有 p 1 p1 p1贡献,满足 ( j − i &gt; 1 (j-i&gt;1 (ji>1 k i &lt; max ⁡ x = i + 1 j − 1 &lt; k j k_i&lt;\max_{x=i+1}^{j-1}&lt;k_j ki<maxx=i+1j1<kj k j &lt; max ⁡ x = i + 1 j − 1 &lt; k i ) k_j&lt;\max_{x=i+1}^{j-1}&lt;k_i) kj<maxx=i+1j1<ki)就有 p 2 p2 p2贡献

这个题本来也想用莫队做,但实际只能做到 O ( n n log ⁡ n ) O(n\sqrt n\log n) O(nn logn),会被卡,要找到一个 O ( n log ⁡ n ) O(n\log n) O(nlogn)的做法,我们可以先用单调栈计算出每个点左边第一个比它大的数和每个点右边第一个比它大的数,设为 L i , R i L_i,R_i Li,Ri,那么对于每对 L i , R i L_i,R_i Li,Ri一定会产生 p 1 p1 p1的贡献,对于每个相邻的 i , i + 1 i,i+1 i,i+1也会产生 p 1 p1 p1的贡献,然后对于每个数来说, L i L_i Li会与 [ i + 1 , R i − 1 ] [i+1,R_i-1] [i+1,Ri1]产生贡献, R i R_i Ri会与 [ L i + 1 , i − 1 ] [L_i+1,i-1] [Li+1,i1]产生贡献,我们可以把所有询问离线下来,依次加入每个位置的贡献,类似于差分的思想加入 l − 1 l-1 l1的时候减去 [ l , r ] [l,r] [l,r]的贡献,加入 r r r的时候加回来。对于这些贡献 p 1 p1 p1的我们在加入 R i R_i Ri的时候更新 L i L_i Li的贡献,贡献 p 2 p2 p2的我们在加入那个固定点的时候更新一段区间的贡献,那么用一个支持区间修改区间求和的线段树就可以了,复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值