[ARC115E] LEQ and NEQ 题解
我这场打的 VP,结果 E 思考的时间比 A 还少。。
但是我觉得我能想出这道题还是很有意义的,写篇题解记录一下。
首先应该都不难想到动态规划吧?我们先使用暴力 DP:设 \(dp_{i,j}\) 表示处理完前 \(i\) 个数,第 \(i\) 个数为 \(j\) 的方案数。我们考虑进行分类讨论:
- \(a_i≥a_{i-1}\):此时 \(j\) 有两种取值范围,分别是 \(1\leq j\leq a_{i-1}\) 以及 \(a_{i-1}<j\leq a_i\)。对于后者的范围,我们发现 \(j\) 怎么取都不会影响到 \(i-1\) 的取值,我们设 \(f_i=\sum_{1\leq j\leq a_i}dp_{i,j}\),因此这一部分的 \(dp_{i,j}=f_{i-1}\)。对于 \(1\leq j\leq a_{i-1}\),我们就要考虑到 \(x_{i-1}≠x_i\) 的限制。根据容斥原理,我们用 \(f_{i-1}\) 减去 \(dp_{i-1,j}\) 的情况就行了,于是得到下面的转移:
- \(a_i<a_{i-1}\):这个时候无论 \(j\) 取 \([1,a_i]\) 的什么值都有可能受到 \(x_{i-1}\) 的影响。不过思路还是用上面的容斥,我们得到:
知道这些之后,我们要得到的答案就是:
因此,若要在题目所给的数据范围限制下快速得到答案,我们必须想办法快速处理出 \(f_i\) 的值。
0x01
对于 \(a_i≥a_{i-1}\) 的情况,我们直接加和:
根据 \(f_i\) 的定义可知,等式右边减去的式子其实就是 \(f_{i-1}\),因此合并同类项得到:
0x02
对于 \(a_i<a_{i-1}\) 的情况,我们同样进行数学计算:
化简最多也就能变成这样,内层的求和还是不能省去。本题有价值的思路来了:我们想象一下 \(\sum_{j=1}^{a_i}dp_{i-1,j}\) 的实际意义。我们假设现在的 \(a_{i-1}\) 不再是原来的 \(a_{i-1}\),而是被强制修改为了 \(a_i\) 的值,那么 \(\sum_{j=1}^{a_{i-1}}dp_{i-1,j}=f_{i-1}\) 其实就是 \(\sum_{j=1}^{a_i}dp_{i-1,j}\)。
于是 \(\sum_{j=1}^{a_i}dp_{i-1,j}\) 就相当于把 \(a_{i-1}\) 换成 \(a_i\) 之后所处理出来的 \(f_{i-1}\),令这个 \(f_{i-1}\) 为 \(f'_{i-1}\)。所以:
我们再继续展开,去考虑 \(f'_{i-1}\) 的求法。其实就是不断重复上述过程进行递归,直到当前已经递归到了边界或者 \(a_{i-1}\leq a_i\) 的情况。
但是极限数据不允许我们暴力递归。我们令 \(f'_j\) 表示当 \(a_j=a_i\) 时处理出来的 \(f_j\)。为了方便分析,我们造一组数据 \(a=\{1,3,3,3,2\}\)。接着,我们按照上述方法求 \(f_5\):
结合上述推理,我们可以使用单调栈求出 \([1,i-1]\) 中最靠后的 \(\leq a_i\) 的元素下标 \(l_i\),则有:
不难发现可以通过将 \(i\) 分为奇数和偶数去处理前缀和,然后求出 \(f_{i-1}-f_{i-2}+f_{i-3}\dots\) 的值,后面的 \(f_{l_i}\times (a_i-1)\) 的正负判断也可以利用 \(i-l_i\) 的奇偶。
最后注意一下各种取模就行了,代码也比较短小:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
const int MOD=998244353;
int n,a[MAXN];
long long dp[MAXN],sum[MAXN],num[MAXN];
int stk[MAXN],cnt,l[MAXN];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
while(cnt&&a[stk[cnt]]>a[i]) cnt--;
l[i]=stk[cnt],stk[++cnt]=i;
}
dp[1]=a[1]%MOD,sum[1]=a[1]%MOD;
for(int i=2;i<=n;i++)
{
if((i-1)&1) dp[i]=sum[i-1]+num[l[i]]-sum[l[i]]-num[i-1]+MOD*2,dp[i]%=MOD;
else dp[i]=num[i-1]+sum[l[i]]-num[l[i]]-sum[i-1]+MOD*2,dp[i]%=MOD;
dp[i]*=a[i],dp[i]%=MOD;
int g=((i-l[i]-1)&1)?-1:1;
if(!l[i]) dp[i]+=g*a[i],dp[i]+=MOD,dp[i]%=MOD;
else dp[i]+=dp[l[i]]*(a[i]-1)%MOD*g,dp[i]+=MOD,dp[i]%=MOD;
if(i&1) sum[i]=sum[i-2]+dp[i],num[i]=num[i-1];
else num[i]=num[i-2]+dp[i],sum[i]=sum[i-1];
sum[i]%=MOD,num[i]%=MOD;
}
cout<<dp[n];
return 0;
}