春季算法练习1004

#include <bits/stdc++.h>
/*杭电春季赛1 1004 海浪
    st表
    将海浪条件转化为偶数位置最小值大于奇数位置最大值并取反后奇数位置最小值大于偶数位置最大值
*/
#define int long long
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, base = 261, inf = 1e18, mod = 1e9 + 7, MOD = 998244353, INF = (1 << 30) - 1;

int n, q, a[N], ql[N], qr[N];
int lg[N], mx[19][N], mi[19][N];
int pos[N], len[19][N], ans[N];

void Read() {
	cin >> n >> q;
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	for (int i = 1; i <= q; i ++ ) cin >> ql[i] >> qr[i];
}

void initst() {
	for (int i = 2; i <= n; i ++ ) lg[i] = lg[i >> 1] + 1;
	//预处理每个区间长度的对数,用于快速计算区间长度
	for (int i = 1; i <= n; i ++ ) {
		mx[0][i] = i & 1 ? -INF : a[i];
		//奇数位置的最大值初始化为-inf,最小值为a[i]
		//偶数位置的最大值初始化为a[i],最小值为inf
		mi[0][i] = i & 1 ? a[i] : INF;
	}
	for (int i = 1; i < 19; i ++ ) {
		for (int j = 1; j + (1 << i) - 1 <= n; j ++ ) {
			mx[i][j] = max(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
			mi[i][j] = min(mi[i - 1][j], mi[i - 1][j + (1 << (i - 1))]);
		}
	}
	//递推计算每一层的最大值最小值
}

bool query(int l, int r) {
	int t = lg[r - l + 1];//区间长度的对数
	int u = min(mi[t][l], mi[t][r - (1 << t) + 1]);
	int d = max(mx[t][l], mx[t][r - (1 << t) + 1]);
	//查询区间的最大值最小值
	return u > d;//需要偶数位置的最小值大于奇数位置的最大值
}

void initlen() {
	for (int i = 1; i < 19; i ++ ) {
		for (int j = 1; j + (1 << i) - 1 <= n; j ++ )
			len[i][j] = max(len[i - 1][j], len[i - 1][j + (1 << (i - 1))]);
	}
}

int querylen(int l, int r) {
	int t = lg[r - l + 1];
	return max(len[t][l], len[t][r - (1 << t) + 1]);
}

void Reverse() {
	for (int i = 1; i <= n; i ++ ) a[i] = -a[i];
}

void solve() {
	initst();
	for (int i = 1, j = 1; i <= n; i ++ ) {
		for (; j <= i && !query(j, i); j ++ );
		//使用双指针计算每个右端点i对应的最小左端点j
		pos[i] = j;//pos[x] 表示以x为右端点的海浪区间的最小左端点。
		len[0][i] = i - j + 1;
	}
	initlen();
	for (int i = 1; i <= q; i ++ ) {
		int l = ql[i], r = qr[i];
		int x = lower_bound(pos + l, pos + r + 1, l) - pos;
		//用于在 pos+l 到 pos+r+1 的范围内找到第一个大于等于 l 的位置
		//x是满足pos[x] >= l的第一个端点
		ans[i] = max(ans[i], x - l);//相当于(x - 1 + l + 1)
		//第一种情况:右端点在查询区间内,但最小左端点在查询区间左侧
		if (x <= r)
			ans[i] = max(ans[i], querylen(x, r));
		//第二种情况:右端点和最小左端点都在区间内
	}
}

void out() {
	int R = 0;
	for (int i = 1; i <= q; i ++ ) {
		R = (R + 1LL * i * ans[i]) % 1000000007;
	}
	cout << R << endl;
	for (int i = 1; i <= q; i ++ ) ans[i] = 0;
}

void wyx() {
	Read();
	solve();
	Reverse();
	solve();
	out();
}

signed main() {
	ios::sync_with_stdio(false); cin.tie(0);
	int t = 1; cin >> t; 
	while (t--) {wyx();} return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值