CF1591F

题意:给一个长度为 \(n\)\(n\le 2\times 10^5\) )的数列 \(a\),求一个长度为 \(n\)\(b\) 数组,满足 \(b_i \le a_i\) ,且相邻两项不相等的 \(b\) 的方案数,对 \(998244353\) 取模

考虑容斥,设 \(f_i\) 表示至少有 \(i\) 组相邻的数相等 \(ans=\sum f_i\times (-1)^i\)

所以跟相邻相等的数的奇偶有关,也就是和按照相同的值分成奇数段,偶数段有关 ,所以将状态改为 \(f_{i,0/1}\) 表示前 \(i\) 个数分成偶数/奇数段的方案数,得到朴素dp:

\(f_{i,0/1}=\sum f_{j,1/0}\times \min\{a_j,a_{j+1},...,a_i\}\) (因为是至少所以不用考虑与前面那一段选的数一样导致没有分出来新的一段的情况)

时间复杂度 \(O(n^2)\) 从那一坨 \(\min\) 入手进行优化,于是整个单调栈,每次找到前面第一个比 \(a_i\) 小的位置 \(x\) ,这样的话对于 \(i\)\(x\) 之前的贡献其实就是 \(f_{x,0/1}\) (因为在 \(x\) 之前的贡献的 从 \(j\)\(i\)\(\min\) 可以替换成从 \(j\)\(x\)\(\min\) ,然后代入式子就变成了 \(f_{x,0/1}\)), 对于 \(x\) 之后的,\(\min\) 值都是 \(a_i\) ,所以直接维护前缀和即可

code:

#include <bits/stdc++.h>
#define int long long
#pragma GCC optimeze(3)
#pragma GCC optimeze(2)
using namespace std;
const int N=2e5+10;
const int mod=998244353;
int n,a[N],stk[N],top,f[N][2],s[N][2];
signed main(){ 
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	f[0][0]=s[0][0]=1;
	for(int i=1;i<=n;i++){
		while(top&&a[stk[top]]>=a[i])top--;
		stk[++top]=i;
		f[i][0]=((top==1? 0:f[stk[top-1]][0])+(s[i-1][1]-(top==1?0:s[stk[top-1]-1][1])+mod)*a[i])%mod;
		f[i][1]=((top==1? 0:f[stk[top-1]][1])+(s[i-1][0]-(top==1?0:s[stk[top-1]-1][0])+mod)*a[i])%mod;
		s[i][0]=(s[i-1][0]+f[i][0])%mod;
		s[i][1]=(s[i-1][1]+f[i][1])%mod;
	}
	cout<<((f[n][0]-f[n][1]+mod)%mod*(n%2? -1:1)+mod)%mod;
	return 0;
}
posted @ 2025-02-14 11:14  Xdik  阅读(9)  评论(0)    收藏  举报