[ARC115E] LEQ and NEQ 题解

我这场打的 VP,结果 E 思考的时间比 A 还少。。

但是我觉得我能想出这道题还是很有意义的,写篇题解记录一下。

首先应该都不难想到动态规划吧?我们先使用暴力 DP:设 dpi,j 表示处理完前 i 个数,第 i 个数为 j 的方案数。我们考虑进行分类讨论:

  • aiai1:此时 j 有两种取值范围,分别是 1jai1 以及 ai1<jai。对于后者的范围,我们发现 j 怎么取都不会影响到 i1 的取值,我们设 fi=1jaidpi,j,因此这一部分的 dpi,j=fi1。对于 1jai1,我们就要考虑到 xi1xi 的限制。根据容斥原理,我们用 fi1 减去 dpi1,j 的情况就行了,于是得到下面的转移:

dpi,j={fi1dpi1,j1jai1fi1ai1<jai

  • ai<ai1:这个时候无论 j[1,ai] 的什么值都有可能受到 xi1 的影响。不过思路还是用上面的容斥,我们得到:

dpi,j=fi1dpi1,j

知道这些之后,我们要得到的答案就是:

ans=i=1nj=1aidpi,j=i=1nfi

因此,若要在题目所给的数据范围限制下快速得到答案,我们必须想办法快速处理出 fi 的值。

0x01

对于 aiai1 的情况,我们直接加和:

fi=j=1ai1(fi1dpi1,j)+j=ai1+1aifi1=ai1×fi1+(aiai1)×fi1j=1ai1dpi1,j

根据 fi 的定义可知,等式右边减去的式子其实就是 fi1,因此合并同类项得到:

fi=fi1×(ai1)

0x02

对于 ai<ai1 的情况,我们同样进行数学计算:

fi=j=1ai(fi1dpi1,j)=ai×fi1j=1aidpi1,j

化简最多也就能变成这样,内层的求和还是不能省去。本题有价值的思路来了:我们想象一下 j=1aidpi1,j 的实际意义。我们假设现在的 ai1 不再是原来的 ai1,而是被强制修改为了 ai 的值,那么 j=1ai1dpi1,j=fi1 其实就是 j=1aidpi1,j

于是 j=1aidpi1,j 就相当于把 ai1 换成 ai 之后所处理出来的 fi1,令这个 fi1fi1。所以:

fi=ai×fi1fi1

我们再继续展开,去考虑 fi1 的求法。其实就是不断重复上述过程进行递归,直到当前已经递归到了边界或者 ai1ai 的情况。

但是极限数据不允许我们暴力递归。我们令 fj 表示当 aj=ai 时处理出来的 fj。为了方便分析,我们造一组数据 a={1,3,3,3,2}。接着,我们按照上述方法求 f5

f5=a5×f4f4=a5×f4a5×f3+f3=a5×f4a5×f3+a5×f2f2=a5×f4a5×f3+a5×f2f1×(a51)=a5×(f4f3+f2)f1×(a51)

结合上述推理,我们可以使用单调栈求出 [1,i1] 中最靠后的 ai 的元素下标 li,则有:

fi=ai×(fi1fi2+fi3±fli+1)[±fli×(ai1)]

不难发现可以通过将 i 分为奇数和偶数去处理前缀和,然后求出 fi1fi2+fi3 的值,后面的 fli×(ai1) 的正负判断也可以利用 ili 的奇偶。

最后注意一下各种取模就行了,代码也比较短小:

#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;
}
posted @   Supor__Shoop  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 2025成都.NET开发者Connect圆满结束
· 后端思维之高并发处理方案
· 千万级大表的优化技巧
· 在 VS Code 中,一键安装 MCP Server!
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析
Document
点击右上角即可分享
微信分享提示