leetcode第294场周赛巫师的总力量和——维护前缀和的前缀和

Posted hesorchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode第294场周赛巫师的总力量和——维护前缀和的前缀和相关的知识,希望对你有一定的参考价值。

主要记录一下On预处理,O1取任意区间前缀和的前缀和

题目

6077. 巫师的总力量和

解题思路

贪心从小到大考虑,每次计算以 a i a_i ai为最小值时的贡献。

下面来看具体例子:

  1. 考虑 a 2 a_2 a2作为最小值时的贡献
    区间 [ 1 , 2 ] , [ 1 , 3 ] , [ 1 , 4 ] , [ 1 , 5 ] , [ 2 , 2 ] , [ 2 , 3 ] , [ 2 , 4 ] , [ 2 , 5 ] [1,2],[1,3],[1,4],[1,5],[2,2],[2,3],[2,4],[2,5] [1,2],[1,3],[1,4],[1,5],[2,2],[2,3],[2,4],[2,5]
  2. 考虑 a 4 a_4 a4作为最小值时的贡献
    区间 [ 3 , 4 ] , [ 4 , 4 ] , [ 4 , 5 ] , [ 3 , 5 ] [3,4],[4,4],[4,5],[3,5] [3,4],[4,4],[4,5],[3,5]
    如果我们计算区间 [ 1 , 4 ] [1,4] [1,4] [ 2 , 4 ] [2,4] [2,4],则与1中的贡献计算发生了重复。因此每次计算 a i a_i ai的贡献时,左端点不超过上个值与 a i a_i ai相同的位置,右端点到 n n n,但是,这样还不够,我们还要保证这些区间的最小值为 a i a_i ai
  3. 考虑 a 3 a_3 a3作为最小值时的贡献
    区间 [ 3 , 3 ] [3,3] [3,3]合法,因为之前枚举过的 a i a_i ai都小于当前值,会分割数组。

接下来考虑如何计算区间贡献。我们不可能枚举所有区间,这样会超时,来观察上述步骤1中的贡献情况。


容易发现,以 a i a_i ai为最小值的时候,左边元素的贡献次数为 1 次 , 2 次 , 3 次 . . . . . . 1次,2次,3次... ... 1,2,3......,左边元素的贡献次数为 . . . . . . 3 次 , 2 次 , 1 次 ... ...3次,2次,1次 ......3,2,1 a i a_i ai本身贡献次数为 ( n u m l e f t + 1 ) × ( n u m r i g h t + 1 ) (num_left+1)\\times(num_right+1) (numleft+1)×(numright+1)次。只需要确定区间的左右端点,就可以直接计算以 a i a_i ai为最小值的所有区间的贡献。

a i a_i ai两侧的贡献次数,满足前缀和的前缀和规律。右侧部分是从左向右的前缀和的前缀和。左侧部分是从右向左的后缀和的后缀和。如何快速计算任意区间的前缀和的前缀和呢?

来看下图。

初始化 : s 0 = s s 0 = 0 s_0 = ss_0=0 s0=ss0=0
前缀和: s i = s i − 1 + a i s_i = s_i-1+a_i si=si1+ai
前缀和的前缀和: s s i = s s i − 1 + s i ss_i = ss_i-1+s_i ssi=ssi1+si

结论:

s s l , r = s s r − s s l − 1 − ( r − l + 1 ) × s l − 1 ss_l,r=ss_r-ss_l-1-(r-l+1)\\times s_l-1 ssl,r=ssrssl1(rl+1)×sl1

很容易验证结论的正确性,不再赘述。

这样一来,我们就可以 O ( n ) O(n) O(n)预处理之后, O ( 1 ) O(1) O(1)得到任意区间的前缀和的前缀和

后缀同理。

AC代码

const long long N = 1e5 + 5;
const long long mod = 1e9 + 7;

class Solution

public:
	map<long long, vector<long long>> pos;
	long long pre[N], suf[N], ppre[N], ssuf[N];
	set<long long> st;
	set<long long> vis;
	long long n;

	void init_ss(vector<int> &a)
	
		int n = a.size() - 1;
		for (int i = 1; i <= n; i++)
			pre[i] = pre[i - 1] + a[i];
		for (int i = 1; i <= n; i++)
			ppre[i] = (ppre[i - 1] + pre[i]) % mod;
		for (int i = n; i >= 1; i--)
			suf[i] = suf[i + 1] + a[i];
		for (int i = n; i >= 1; i--)
			ssuf[i] = (ssuf[i + 1] + suf[i]) % mod;
	

	long long getlr(int l, int r)
	
		long long len = r - l + 1;
		return (ppre[r] - (ppre[l - 1] + pre[l - 1] * len) % mod + mod) % mod;
	
	long long getrl(int r, int l)
	
		long long len = r - l + 1;
		return (ssuf[l] - (ssuf[r + 1] + suf[r + 1] * len) % mod + mod) % mod;
	

	long long totalStrength(vector<int> &a)
	

		n = a.size();
		a.insert(a.begin(), -1);

		init_ss(a);

		vis.insert(0);
		vis.insert(n + 1);

		for (long long i = 1; i <= n; i++)
		
			st.insert(a[i]);
			pos[a[i]].emplace_back(i);
		

		long long ans = 0;
		for (auto val : st)
		
			long long m = pos[val].size();
			for (long long i = 0; i < m; i++)
			
				long long p = pos[val][i];
				auto iter = vis.upper_bound(p);
				long long rr = (*iter) - 1;
				iter--;
				long long ll = (*iter) + 1;

				long long lenL = p - ll + 1;
				long long lenR = rr - p + 1;

				long long num, v;
				num = lenL;
				v = getlr(p, rr);
				ans = (ans + num * v % mod * val) % mod;
				num = lenR;
				v = getrl(p, ll);
				ans = (ans + num * v % mod * val) % mod;
				num = lenR * lenL % mod;
				v = val;
				ans = (ans - num * v % mod * val % mod + mod) % mod; //减去重复贡献
				vis.insert(p);
			
		
		return ans;
	
;

以上是关于leetcode第294场周赛巫师的总力量和——维护前缀和的前缀和的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 6077. 巫师的总力量和

LeetCode第302场周赛

LeetCode第302场周赛

LeetCode第302场周赛

LeetCode 第 183 场周赛

LeetCode周赛第 291 场周赛(Go语言实现版)