CF1585F Non-equal Neighbours - 容斥 - dp - 单调栈
题目链接:https://codeforces.com/problemset/problem/1585/F
题解:
难难难
考虑容斥:设 表示 () 时对应的 方案的答案
那么答案就是
后者可以用容斥原理化简。也就是这个式子:
考虑这个过程的意义: 的含义就是我钦定了 ,然后剩下的随便选(也就是没有限制了), 的含义就是钦定了 ,然后剩下的没有限制。
那这个贡献如何算呢?我们发现,如果有 的话,那么这段的贡献就是三个数的 min,再和别的部分相乘。
可以将每一个相同的连续部分看成一“段”,如果已经钦定了 个连续的部分,那相当于有 个“段”,考虑对段的贡献进行 dp。
设 表示考虑到第 个位置,当前划分了 个段的贡献,那么显然
注意这里“段”的意义是我“钦定”的段。一个例子:我可以将 划分为 ,段内的元素一定是相同的,这是我钦定的,但是段间的元素也可能相同,这是我钦定之后没有其它限制条件的结果。例如 是合法的。
然后由于容斥系数的正负只和 的奇偶性有关,可以变成 ,再利用单调栈记录一下上一个比当前位置小的位置,写出来转移发现可以化简。后面的部分和这篇博客差别不大。
代码:
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5, mod =998244353;
int n,a[maxn];
int dp[maxn][2], pre[maxn][2];
stack<int>stk;
signed main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
dp[0][0] = pre[0][0] = 1;
for(int i=1;i<=n;i++){
while(!stk.empty() && a[stk.top()] >= a[i])stk.pop();
if(stk.empty()){
dp[i][0] = 1ll * pre[i-1][1] * a[i] % mod;
dp[i][1] = 1ll * pre[i-1][0] * a[i] % mod;
}else{
int p = stk.top();
for(int j=0;j<=1;j++){
(dp[i][j] = 1ll*dp[p][j] + 1ll*(pre[i-1][j^1]-pre[p-1][j^1]+mod)*a[i]%mod)%=mod;
}
}
for(int j=0;j<=1;j++)
(pre[i][j] = pre[i-1][j] + dp[i][j])%=mod;
stk.push(i);
}
int sgn = (n&1) ? -1 : 1;
printf("%d\n",((dp[n][0]-dp[n][1])*sgn+mod)%mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示