Loading

P10173 「OICon-02」maxiMINImax (单调栈+树状数组)

P10173 「OICon-02」maxiMINImax

首先观察所求的式子,我们可以很容易发现 \(\min_{[l_2,l_2]}>\max(\max_{[l_1,r_1]},\max_{[l_3,r_3]})\),否则贡献一定为 \(0\)

此时如果要考虑枚举其中一个区间,我们肯定选择中间的 \([l_2,r_2]\),但是这样复杂度仍然至少是 \(O(n^2)\)

我们思考是否真的需要枚举区间。枚举区间的意义是能够知道 \([l_1,r_1]\)\([l_3,r_3]\) 的区间范围,达到题目条件中的三个区间不交。但是我们发现,假如 \([l_2,r_2]\)\([l_1,r_1]\) 相交,那么在相交区间内一定存在一个位置 \(p\),使得 \(\min_{[l_2,r_2]}\le a_p\le \max_{[l_1,r_1]}\),无法满足第一个条件。

此时我们知道,只需要满足 \(\min_{[l_2,l_2]}>\max(\max_{[l_1,r_1]},\max_{[l_3,r_3]})\),区间一定不交。

考虑计算贡献,我们容易用单调栈预处理出每个位置作为最大值/最小值的区间数 \(c_i/d_i\)

此时枚举 \(a_i\) 作为 \(\min_{[l_2,l_2]}\),所有位置的答案即为 \(\sum c_{min2}d_{max1}d_{max3}(min2-max1)(min2-max3)\)

如何维护这个东西?考虑拆开式子,发现只需要维护 \(\sum c\)\(\sum d\)\(\sum cmax\)\(\sum dmax\)

我们在计算时要满足第一个条件,这里用一个技巧,就是把数从小到大放进去同时维护树状数组即可,当然也可以用权值线段树分别维护前缀和后缀。

复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
typedef long long i64;
#define pii std::pair<i64, i64>
#define fi first
#define se second

const int N = 1000010;
const int mod = 9712176;
i64 a[N], s[N];
i64 lmin[N], lmax[N], rmin[N], rmax[N], minn[N], maxn[N]; 
i64 n;
int lowbit(int x) {return x & (-x);} 
struct bit {
	i64 c[N];
	void add(int x, i64 y) {
		for(int i = x; i <= n; i += lowbit(i)) {
			c[i] = (c[i] + y) % mod;
		}
	}
	i64 qmax(int x) {
		if(!x) return 0;
		i64 ret = 0;
		for(int i = x; i; i -= lowbit(i)) {
			ret = (ret + c[i]) % mod;
		}
		return ret;
	}
}t1, t2;
void Solve() {
	std::cin >> n;

	for(int i = 1; i <= n; i++) {
		std::cin >> a[i];
	}

	int top = 0;
	for(int i = 1; i <= n; i++) {
		while(top && a[s[top]] >= a[i]) top--;
		lmin[i] = s[top] + 1;
		s[++top] = i;
	}
	top = 0;
	for(int i = 1; i <= n; i++) {
		while(top && a[s[top]] <= a[i]) top--;
		lmax[i] = s[top] + 1;
		s[++top] = i;
	}
	top = 0;
	for(int i = n; i >= 1; i--) {
		while(top && a[s[top]] >= a[i]) top--;
		if(!top) rmin[i] = n;
		else rmin[i] = s[top] - 1;
		s[++top] = i;
	}
	top = 0;
	for(int i = n; i >= 1; i--) {
		while(top && a[s[top]] <= a[i]) top--;
		if(!top) rmax[i] = n;
		else rmax[i] = s[top] - 1;
		s[++top] = i;
	}
	for(int i = 1; i <= n; i++) {
		minn[i] = 1ll * (i - lmin[i] + 1) * (rmin[i] - i + 1) % mod;
		maxn[i] = 1ll * (i - lmax[i] + 1) * (rmax[i] - i + 1) % mod;
	}

	std::vector<pii> b(n + 1);
	for(int i = 1; i <= n; i++) {
		b[i].fi = a[i], b[i].se = i;
	}

	std::sort(b.begin() + 1, b.end());

	i64 ans = 0;
	for(int i = 1; i <= n; i++) {
		i64 dmx1 = t1.qmax(b[i].se - 1);
		i64 dmx3 = (t1.qmax(n) - t1.qmax(b[i].se) + mod) % mod;
		i64 dmx1mx1 = t2.qmax(b[i].se - 1);
		i64 dmx3mx3 = (t2.qmax(n) - t2.qmax(b[i].se) + mod) % mod;
		// std::cout << dmx1 << " " << dmx3 << " " << dmx1mx1 << " " << dmx3mx3 << "\n";
		ans = (ans + b[i].fi * b[i].fi % mod * dmx1 % mod * dmx3 % mod * minn[b[i].se] % mod) % mod;
		ans = (ans - b[i].fi * dmx3mx3 % mod * dmx1 % mod * minn[b[i].se] % mod + mod) % mod;
		ans = (ans - b[i].fi * dmx1mx1 % mod * dmx3 % mod * minn[b[i].se] % mod + mod) % mod;
		ans = (ans + dmx1mx1 * dmx3mx3 % mod * minn[b[i].se] % mod) % mod;
		t1.add(b[i].se, maxn[b[i].se]);
		t2.add(b[i].se, maxn[b[i].se] * b[i].fi % mod);
	}

	std::cout << ans << "\n";
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	Solve();

	return 0;
}
posted @ 2024-03-23 20:35  Fire_Raku  阅读(20)  评论(0编辑  收藏  举报