2019 计蒜之道 初赛 第五场 浪潮面试题之数组(单调栈)

题目链接

题目大意:

思路:用单调栈维护最优决策的集合。
我们倒着遍历数组,每次遍历到的相当于 i i i, 每次新建一个决策 { a i , l i , r i , x } \left\{ a_i,l_i,r_i,x\right\} {ai,li,ri,x},意为:当前最优 x x x位置的时候包含的右端点区间为 l i , r i l_i,r_i li,ri,然后更新是在top的两个决策之间,设两个决策分别为 n e w = { a 1 , l 1 , r 1 , x } , p r e = { a 2 , l 2 , r 2 , y } new=\left\{ a_1,l_1,r_1,x\right\},pre=\left\{ a_2,l_2,r_2,y\right\} new={a1,l1,r1,x},pre={a2,l2,r2,y},那么new的决策右端点r的范围可以扩大到pre的决策的 l 2 , r 2 l_2,r_2 l2,r2之中,我们每次二分一个极大位置 p o s , p o s [ l 2 , r 2 ] pos,pos\in[l_2,r_2] pos,pos[l2,r2]使得 a 1 ( p o s x + 1 ) a 2 ( p o s y + 1 ) a_1*(pos-x+1)\geq a_2*(pos-y+1) a1(posx+1)a2(posy+1),那么我们把新的决策范围扩大到pos即可,每次更新决策把旧的贡献减掉即可。
我们来分析一下二分的单调性:
1.若 a 1 a 2 a_1\geq a_2 a1a2,必然满足
2.若 a 1 &lt; a 2 a_1&lt;a_2 a1<a2,由于 1 x &gt; 1 y 1-x&gt;1-y 1x>1y,我们 p o s pos pos从小到大增加必然是要使得 1 x &gt; 1 y 1-x&gt;1-y 1x>1y造成的影响抵消, p o s pos pos必然是一个极大的值刚好抵消影响,那么 p o s \leq pos pos的位置必然没有抵消完影响。
综上,二分的单调性成立。
细节见代码:

#include<bits/stdc++.h>

#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define LL long long
#define pii pair<int,int>
#define SZ(x) (int)x.size()
#define all(x) x.begin(),x.end()

using namespace std;

LL gcd(LL a, LL b) {return b ? gcd(b, a % b) : a;}
LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
LL powmod(LL a, LL b, LL MOD) {LL ans = 1; while (b) {if (b % 2)ans = ans * a % MOD; a = a * a % MOD; b /= 2;} return ans;}
const int N = 2e5 + 11;
struct uzi
{
	int d, l, r, i;
} Q[N];
int R, n, a[N];
const LL mod = 1e9 + 7;
LL res, tmp;
LL get(int l, int r) {
	return ((l + r + 0ll) * (r - l + 1ll) / 2ll) % mod;
}
int main() {
	ios::sync_with_stdio(false);
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	for (int i = n; i >= 1; i--) {
		for (Q[++R] = {a[i], i, i, i}; R > 1;) {
			auto l = Q[R], r = Q[R - 1];
			if (1ll * l.d * (r.r - l.i + 1) >= 1ll * r.d * (r.r - r.i + 1)) {
				Q[--R] = {a[i], l.l, r.r, l.i};
				tmp += mod - 1ll * r.d * get(r.l - r.i + 1, r.r - r.i + 1) % mod;
				tmp %= mod;
			} else {
				int x = r.l, y = r.r, ans = -1;
				while (x <= y) {
					int mid = x + y >> 1;
					if (1ll * l.d * (mid - l.i + 1) >= 1ll * r.d * (mid - r.i + 1))ans = mid, x = mid + 1;
					else y = mid - 1;
				}
				if (ans == -1)break;
				Q[R] = {l.d, l.l, ans, l.i};
				Q[R - 1] = {r.d, ans + 1, r.r, r.i};
				tmp += mod - 1ll * r.d * get(r.l - r.i + 1, r.r - r.i + 1) % mod;
				tmp += 1ll * r.d * get(ans + 1 - r.i + 1, r.r - r.i + 1) % mod;
				tmp %= mod;
			}
		}
		tmp += 1ll * Q[R].d * get(Q[R].l - Q[R].i + 1, Q[R].r - Q[R].i + 1) % mod;
		tmp %= mod;
		res += tmp;
		res %= mod;
	}
	cout << res << endl;
	return 0;
}
全部评论

相关推荐

像好涩一样好学:这公司我也拿过 基本明确周六加班 工资还凑活 另外下次镜头往上点儿
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-27 10:28
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务