2025-7-12模拟测验
自我评价:又 Tang 完了。
题解
题解中包含题面描述,但不包含大样例。
T1 规则制定
唯一场上通过的题。。。
描述
去题解看简述吧。。。
输入
第一行一个正整数 \(n\),表示序列 \(a\) 的长度。
第二行 \(n\) 个非负整数,表示序列 \(a\)。
输出
一行一个整数,表示答案对 \(2^{32}\) 取模后的结果。
样例
共 \(2\) 组,不包含大样例。
样例 \(1\) 输入
3
0 1 2
样例 \(1\) 输出
1
样例 \(2\) 输入
5
4 2 1 4 3
样例 \(2\) 输出
27
数据范围
对于所有测试点,\(3 \leq n \leq 10^6\),\(0 \leq a_i \leq 10^9\)。
提示:这题并不难。
题解
P.S. 数据范围里的提示是包含于题面的,不是我给的提示。
题意简述:
定义 \(a[l:r]\) 表示序列 \(a\) 中,\(a_l,a_{l+1},\cdots,a_r\) 组成的可重集。定义 \(f(S)\) 表示可重集 \(S\) 去掉最大值与最小值(若有多个最大或多个最小,只去掉一个)后,剩余元素的和。
求 \(\sum\limits_{1 \leq l \le r \leq n,r-l+1 \geq 3} f(a[l:r]) \bmod 2^{32}\) 的值。
对于 \(\forall i \in [1,n]\),我们分析 \(a_i\) 产生的贡献。
为了方便,我们先定义一个函数 \(g(l, r, k)\),表示 \([l,r]\) 这段区间中,有多少个子区间(长度 \(\geq 3\))包含第 \(k\) 个元素。
我们考虑用 \(a_i\) 产生的总贡献(即最大值与最小值也产生贡献),减去 \(a_i\) 产生的非法贡献(即 \(a_i\) 为区间最大值或最小值时的贡献),得到 \(a_i\) 的真实贡献。
显然,包含 \(a_i\) 的区间有 \(g(1, n, i)\) 个,所以 \(a_i\) 的总贡献为 \(a_i \times g(1, n, i)\)。
接下来,考虑 \(a_i\) 的非法贡献。如果一个贡献不符合要求,当且仅当 \(a_i\) 为区间最大值或最小值。
我们假设 \(\min_l,\min_r,\max_l,\max_r\) 分别表示 \(a_i\) 左侧第一个比 \(a_i\) 小的元素下标、\(a_i\) 右侧第一个比 \(a_i\) 小的元素下标、\(a_i\) 左侧第一个比 \(a_i\) 大的元素下标、\(a_i\) 右侧第一个比 \(a_i\) 大的元素下标。
则有:\(a_i\) 产生的非法贡献为 \(a_i \times (g(\min_l+1,\min_r-1,i)+g(\max_l+1,\max_r-1,i))\)。因为 \(\min_l,\min_r,\max_l,\max_r\) 规定了使 \(a_i\) 产生非法贡献的区间范围。
综上,\(a_i\) 的真实贡献为:\(a_i \times (g(1, n, i)-g(\min_l+1,\min_r-1,i)-g(\max_l+1,\max_r-1,i))\)。
下面考虑几个细节问题:
-
最大值或最小值有可能重复。解决方法:比较两个元素大小时,先比较具体数值,如果相等则比较下标,下标更大则元素更大。由于下标不重复,所以这样比较不会出现元素相等。显然,这样规定不会影响答案。
-
如何求 \(\min_l,\min_r,\max_l,\max_r\)?这个很容易,如果不会就去学单调栈。
-
如何实现 \(g(l,r,k)\) 函数?首先特判不满足 \(l \leq k \leq r\) 的情况。考虑到 \(k\) 左侧有 \(k-l\) 个元素,右侧有 \(r-k\) 个元素,但需要减去 包含 \(k\) 且长度小于 \(3\) 的区间,因此 \(g(l,r,k) = (k-l+1) * (r-k+1) - [l\not=k] - [r\not=k] - 1\)。这个表达式中 \([\cdots]\) 的含义:若命题 \(p\) 为真,则 \([p] = 1\),否则 \([p] = 0\)。具体实现可以参考下方的代码。
-
模 \(2^{32}\) 可以使用 C++ 中的
unsigned int自然溢出实现。
于是,我们在 \(\mathcal{O}(n)\) 的时间复杂度内解决了问题。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
using uint = unsigned int;
int n, a[1000001], m[1000001][4]; //m: 0 = minl, 1 = minr, 2 = maxl, 3 = maxr
inline uint g(int l, int r, int k){return max((uint)(k-l+1)*(r-k+1)-(l!=k)-(r!=k)-1, 0u);} //g(l,r,k) 函数
inline bool cmp(int x, int y){return (a[x]==a[y]?x<y:a[x]<a[y]);} //比较 a[x] 是否小于 a[y]
signed main(){
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
//单调栈求解 m 数组
stack<int> s;
for(int i = 1; i <= n; i++){
while(!s.empty() && cmp(i, s.top())) s.pop();
m[i][0] = (s.empty()?0:s.top());
s.push(i);
} while(!s.empty()) s.pop();
for(int i = n; i >= 1; i--){
while(!s.empty() && cmp(i, s.top())) s.pop();
m[i][1] = (s.empty()?n+1:s.top());
s.push(i);
} while(!s.empty()) s.pop();
for(int i = 1; i <= n; i++){
while(!s.empty() && cmp(s.top(), i)) s.pop();
m[i][2] = (s.empty()?0:s.top());
s.push(i);
} while(!s.empty()) s.pop();
for(int i = n; i >= 1; i--){
while(!s.empty() && cmp(s.top(), i)) s.pop();
m[i][3] = (s.empty()?n+1:s.top());
s.push(i);
} while(!s.empty()) s.pop();
uint ans = 0;
for(int i = 1; i <= n; i++) ans += a[i] * (g(1, n, i)-g(m[i][0]+1, m[i][1]-1, i)-g(m[i][2]+1, m[i][3]-1, i));
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号