HNOI2016序列+資料加強版(字首和+單調棧)

WAautomaton發表於2019-02-18

普通版:題目連結
資料加強版:題目連結
資料加強版 加強版:題目連結

題目大意

給定一個數列,每次給出一個區間,求區間中所有子段的最小值之和。
n,m105n,m\le 10^5

題解

顯然是單調棧先跑一遍,然後接下來有若干個做法:

1.莫隊

這題莫隊解法也很神啊qwq
考慮加入點時對答案的貢獻,以右端點為例。令pp表示當前詢問區間[l,r][l,r]中的最小值,那麼rr的貢獻實際上就是ap(pl+1)+s[r]s[p]a_p(p-l+1)+s[r]-s[p],其中s[r]s[r]表示區間[1,r][1,r]rr為右端點的貢獻,這個在單調棧的時候就可以dp出來。
於是用st表查詢最小值就可以在O(nn)O(n\sqrt n)的時間內解決了。

2.字首和

我們考慮上面的莫隊演算法,如果詢問區間為[l,r][l,r],令最小值為pp,那麼考慮如果從pp開始不斷往左右兩邊加點,得到的答案為
i=p+1rs[i]s[p](rp)+i=lp1s[i]s[p](pl)+a[p](rp+1)(pl+1)\sum_{i=p+1}^rs[i]-s[p](r-p)+\sum_{i=l}^{p-1}s'[i]-s'[p](p-l)+a[p](r-p+1)(p-l+1)
顯然我們再記一個ss的字首和就可以O(1)O(1)回答詢問了。因此總複雜度為O(nlogn+m)O(nlogn+m),可以做完資料加強版。

3.優化最值查詢

但是資料加強加強版過不了,因為瓶頸卡在了O(nlogn)O(nlogn)的空間複雜度上。接下來就有了一個很神仙的ST表+分塊做法。我們把原數列按照大小為BB分塊,pre[i],suf[i]pre[i],suf[i]分別表示從ii到塊開頭/結尾的最小值位置。
再考慮我們對於這些塊建立ST表,那麼如果左右端點在一個塊內,我們暴力;否則我們O(1)O(1)就可以利用上面那些東西詢問最值。這個做法空間複雜度O(nBlogn)O(\frac nBlogn),時間複雜度O(mB)O(mB),因此BB大概開個10左右就行了。
下面附上加強版加強版的程式碼。

#include <bits/stdc++.h>
namespace IOStream {
	const int MAXR = 10000000;
	char _READ_[MAXR], _PRINT_[MAXR];
	int _READ_POS_, _PRINT_POS_, _READ_LEN_;
	inline char readc() {
	#ifndef ONLINE_JUDGE
		return getchar();
	#endif
		if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
		char c = _READ_[_READ_POS_++];
		if (_READ_POS_ == MAXR) _READ_POS_ = 0;
		if (_READ_POS_ > _READ_LEN_) return 0;
		return c;
	}
	template<typename T> inline void read(T &x) {
		x = 0; register int flag = 1, c;
		while (((c = readc()) < '0' || c > '9') && c != '-');
		if (c == '-') flag = -1; else x = c - '0';
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag;
	}
	template<typename T1, typename ...T2> inline void read(T1 &a, T2&... x) {
		read(a), read(x...);
	}
	inline int reads(char *s) {
		register int len = 0, c;
		while (isspace(c = readc()) || !c);
		s[len++] = c;
		while (!isspace(c = readc()) && c) s[len++] = c;
		s[len] = 0;
		return len;
	}
	inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
	inline void printc(char c) {
		_PRINT_[_PRINT_POS_++] = c;
		if (_PRINT_POS_ == MAXR) ioflush();
	}
	inline void prints(char *s) {
		for (int i = 0; s[i]; i++) printc(s[i]);
	}
	template<typename T> inline void print(T x, char c = '\n') {
		if (x < 0) printc('-'), x = -x;
		if (x) {
			static char sta[20];
			register int tp = 0;
			for (; x; x /= 10) sta[tp++] = x % 10 + '0';
			while (tp > 0) printc(sta[--tp]);
		} else printc('0');
		printc(c);
	}
	template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
		print(x, ' '), print(y...);
	}
}
using namespace IOStream;
using namespace std;
typedef long long ll;

int A, B, C, P;
ll lastans;
inline int rnd() { return A = (A * B + (C ^ (int)(lastans & 0x7FFFFFFF)) % P) % P; }
const int MAXN = 3000005, M = 8, MOD = 1000000007;
int st[20][MAXN / M + 5], sta[MAXN], n, m;
int val[MAXN], lg[MAXN], tta[MAXN];
ll pre1[MAXN], pre2[MAXN], suf1[MAXN], suf2[MAXN];
inline int get_min(int x, int y) { return val[x] < val[y] ? x : y; }
inline int query_min(int l, int r) {
	int tl = (l - 1) / M + 1, tr = (r - 1) / M + 1;
	if (tl == tr) {
		int mn = l;
		for (int i = l + 1; i <= r; i++) if (val[i] < val[mn]) mn = i;
		return mn;
	}
	if ((++tl) > (--tr)) return get_min(tta[l], sta[r]);
	int x = lg[tr - tl + 1];
	return get_min(get_min(st[x][tl], st[x][tr - (1 << x) + 1]), get_min(tta[l], sta[r]));
}
int main() {
	read(n, m);
	for (int i = 1; i <= n; i++) read(val[i]);
	read(A, B, C, P);
	int tp = 0;
	for (int i = 1; i <= n; i++) {
		for (; tp > 0 && val[sta[tp]] >= val[i]; --tp) lg[sta[tp]] = i;
		pre1[i] = pre1[sta[tp]] + (ll)val[i] * (i - sta[tp]);
		pre2[i] = pre2[i - 1] + pre1[i];
		sta[++tp] = i;
	}
	while (tp > 0) lg[sta[tp--]] = n + 1;
	for (int i = n; i > 0; i--) {
		suf1[i] = suf1[lg[i]] + (ll)val[i] * (lg[i] - i);
		suf2[i] = suf2[i + 1] + suf1[i];
	}
	int mm = (n + M - 1) / M;
	for (int i = 1; i <= n; i++) {
		if ((i - 1) % M) sta[i] = val[i] > val[sta[i - 1]] ? sta[i - 1] : i;
		else sta[i] = i;
	}
	for (int i = n; i > 0; i--) {
		if (i % M) tta[i] = val[i] > val[tta[i + 1]] ? tta[i + 1] : i;
		else tta[i] = i;
	}
	for (int i = 1; i <= mm; i++) st[0][i] = sta[min(i * M, n)];
	for (int i = 1; i < 20; i++)
	for (int j = 1; j + (1 << i) - 1 <= mm; j++)
		st[i][j] = get_min(st[i - 1][j], st[i - 1][j + (1 << i >> 1)]);
	lg[1] = 0;
	for (int i = 2; i <= mm; i++) lg[i] = lg[i >> 1] + 1;
	ll res = 0;
	while (m--) {
		int l = rnd() % n + 1, r = rnd() % n + 1;
		if (l > r) swap(l, r);
		int t = query_min(l, r);
		res += (lastans = (ll)(r - t + 1) * (t - l + 1) * val[t] + pre2[r] - pre2[t] - pre1[t] * (r - t) + suf2[l] - suf2[t] - suf1[t] * (t - l)) % MOD;
	}
	printf("%lld\n", (res % MOD + MOD) % MOD);
	return 0;
}

相關文章