[ABC234G] Divide a Sequence
给定长度为 \(N\) 的序列 \(A\),我们定义一种将 \(A\) 划分为若干段的方案的价值为每一段的最大值减去最小值的差的乘积,你需要求出所有划分方案的价值的总和,答案对 \(998244353\) 取模。
- $ 1\ \leq\ N\ \leq\ 3\ \times\ 10^5 $
- $ 1\ \leq\ A_i\ \leq\ 10^9 $
先考虑朴素 \(dp\),设 \(dp_i\) 为划分序列 \(A\) 的前 \(i\) 项所有划分方案的总和,则容易得到转移方程式:
\[dp_i = \sum^{i - 1}_{x = 1} dp_x \times \{ \max^{i}_{k = x + 1}\{a_k\} + \min^{i}_{k = x + 1}\{a_k\} \} \]复杂度 \(O(n^2)\) 考虑优化。
首先拆分式子:
\[dp_i = \sum^{i - 1}_{x = 1}dp_x \times \{\max^{i}_{k = x + 1}\{a_k\}\} + \sum^{i - 1}_{x = 1} dp_x \times \{\min^{i}_{k = x + 1}\{a_k\}\} \]分成 \(max\) 和 \(min\) 的两个子问题处理。
我们这里单单考虑 \(max\) 的情况:
对于 \(dp_i \to dp_{i + 1}\) 转移的情况,容易发现:
\[\max^{i}_{k = x + 1}\{a_k\} \]这个式子的值只会在 \(x > pos\) (其中 \(pos\) 为 \(a_{i + 1}\) 前第一个比 \(a_{i + 1}\) 大的数的下标位置)的那些 \(x\) 值时会改变。
而\(a_{i + 1}\) 前第一个比 \(a_{i + 1}\) 大的数的下标位置我们可以用单调栈 \(O(n)\) 的复杂度预处理完成。
代码:
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
#define int long long
const int MOD = 998244353;
const int MAXN = 3e5 + 7;
int n;
int a[MAXN];
int idmax[MAXN],idmin[MAXN];
stack<int> st;
void init(){
for(int i = 1;i <= n;i++){
while(!st.empty() && a[st.top()] <= a[i]){
st.pop();
}
if(st.empty()) idmax[i] = 0;
else idmax[i] = st.top();
st.push(i);
}
while(!st.empty()) st.pop();
for(int i = 1;i <= n;i++){
while(!st.empty() && a[st.top()] >= a[i]){
st.pop();
}
if(st.empty()) idmin[i] = 0;
else idmin[i] = st.top();
st.push(i);
}
while(!st.empty()) st.pop();
}
int dp[MAXN];
int predp[MAXN];
int maxx[MAXN],minn[MAXN];
signed main(){
// ios::sync_with_stdio(false);
// cin.tie(0),cout.tie(0);
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
init();
dp[0] = predp[0] = 1;
// for(int i = 1;i <= n;i++) cout<<idmin[i]<<" ";
for(int i = 1;i <= n;i++){
if(idmax[i]) maxx[i] = (maxx[idmax[i]] + (predp[i - 1] - predp[idmax[i] - 1]) * a[i]) % MOD;
else maxx[i] = (predp[i - 1] * a[i]) % MOD;
if(idmin[i]) minn[i] = (minn[idmin[i]] + (predp[i - 1] - predp[idmin[i] - 1]) * a[i]) % MOD;
else minn[i] = (predp[i - 1] * a[i]) % MOD;
dp[i] = ((maxx[i] - minn[i]) % MOD + MOD) % MOD;
predp[i] = (predp[i - 1] + dp[i]) % MOD;
}
// for(int i = 1;i <= n;i++) cout<<dp[i]<<" "<<predp[i]<<endl;
cout<<dp[n];
return 0;
}
标签:Divide,Sequence,int,max,leq,st,ABC234G,MAXN,dp
From: https://www.cnblogs.com/wyl123ly/p/18434132