P6604 [HNOI2016] 序列 加强版 题解

与最小值相关,考虑求出每个点左右两边第一个小于这个数的点。可以用笛卡尔树或者单调栈维护。

fif_i 表示以 ii 为右端点的所有区间的最小值之和。考虑如何转移 fif_i

ii 左边第一个小于 aia_i 的位置为 preipre_i,则 fi=fprei+(iprei)×aif_i = f_{pre_i} + (i-pre_i)\times a_i。左端点在 preipre_i 之前的可以直接继承,在这之后的每一区间贡献都是 aia_i

同理设 gig_i 表示以 ii 为左端点的所有区间最小值之和,求法类似。

考虑求答案时,求出区间最小值位置 i[l,r]i \in [l,r],跨过 ii 的贡献为 ai×(il+1)×(ri+1)a_i \times (i-l+1) \times (r-i+1)

考虑求 [l,i)[l,i)(i,r](i,r] 的贡献。比如求 [l,i)[l,i),设 j[l,i)j \in [l,i),考虑以 jj 为左端点,且右端点 [j,i)\in [j,i) 的贡献。可以发现贡献为 gjgig_j - g_i。因为每一个以 ii 为左端点的区间,将左端点拓展到 jj,最小值不变,而相减后剩余的就是 gjgig_j-g_i 了。

同理,(i,r](i,r] 可以用 fjfif_j - f_i 算。维护 ffgg 的前缀和,即可做到 O(nlogn+q)O(n \log n + q) 的复杂度了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <stack>
using namespace std;

const int N = 1e5 + 5;

int n, q, type;
long long a[N];

namespace gen
{
	typedef unsigned long long ull;
	ull s, a, b, c, lastans = 0;
	ull rand()
	{
		return s ^= (a + b * lastans) % c;
	}
};

int pre[N], suf[N];
long long f[N], revf[N];
long long s_f[N], s_revf[N];

int lson[N], rson[N];

int LG2[N];
int ff[N][21], p[N][21];

void Init()
{
	memset(ff, 0x7f, sizeof ff);
	for (int i = 2; i < N; i++) LG2[i] = LG2[i >> 1] + 1;
	for (int i = 1; i <= n; i++) ff[i][0] = a[i], p[i][0] = i;
	for (int j = 1; j <= LG2[n]; j++)
	{
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
		{
			if (ff[i][j - 1] < ff[i + (1 << (j - 1))][j - 1])
			{
				ff[i][j] = ff[i][j - 1];
				p[i][j] = p[i][j - 1];
			}
			else
			{
				ff[i][j] = ff[i + (1 << (j - 1))][j - 1];
				p[i][j] = p[i + (1 << (j - 1))][j - 1];
			}
		}
	}
}

int query(int l, int r)
{
	int x = LG2[r - l + 1];
	return (ff[l][x] < ff[r - (1 << x) + 1][x] ? p[l][x] : p[r - (1 << x) + 1][x]);
}

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> q >> type;
	for (int i = 1; i <= n; i++) cin >> a[i], pre[i] = 0, suf[i] = n + 1;
	if (type == 1) cin >> gen::s >> gen::a >> gen::b >> gen::c;
	stack<int> st;
	for (int i = 1; i <= n; i++)
	{
		while (st.size() && a[st.top()] > a[i])
		{
			lson[i] = st.top();
			suf[st.top()] = i;
			st.pop();
		}
		int p = (st.size() ? st.top() : 0);
		pre[i] = p;
		rson[p] = i;
		st.push(i);
	}
	for (int i = 1; i <= n; i++)
	{
		f[i] = f[pre[i]] + (1LL * i - pre[i]) * a[i];
		s_f[i] = s_f[i - 1] + f[i];
	}
	for (int i = n; i >= 1; i--)
	{
		revf[i] = revf[suf[i]] + (1LL * suf[i] - i) * a[i];
		s_revf[i] = s_revf[i + 1] + revf[i];
	}
	Init();
	unsigned long long res = 0;
	while (q--)
	{
		int l, r;
		if (type == 0) cin >> l >> r;
		else
		{
			l = gen::rand() % n + 1, r = gen::rand() % n + 1;
		}
		if (l > r) swap(l, r);
		int place = query(l, r);
		long long ans = 0;
		ans += 1ll * a[place] * (1ll * r - place + 1) * (1ll * place - l + 1);
		ans += 1ll * (1ll * s_f[r] - s_f[place] - f[place] * 1ll * (1ll * r - place));
		ans += 1ll * (1ll * s_revf[l] - s_revf[place] - revf[place] * (1ll * place - l));
		gen::lastans = (unsigned long long)ans;
		res ^= (unsigned long long)ans;
	}
	cout << res << "\n";
	return 0;
}
posted @   HappyBobb  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示