AtCoder Regular Contest 115 E. LEQ and NEQ(容斥 单调栈优化dp)

2024-01-21 01:04

本文主要是介绍AtCoder Regular Contest 115 E. LEQ and NEQ(容斥 单调栈优化dp),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目

n(n<=5e5)个数,第i个数ai(1<=ai<=1e9)

构造一个序列b,要求bi∈[1,ai],且b[i]不等于b[i+1]

求方案数,答案对998244353取模

思路来源

洛谷题解Xu_brezza

一模一样的cf题:

Codeforces Round 759 (Div. 2, based on Technocup 2022 Elimination Round 3) F. Non-equal Neighbours

题解

首先肯定是容斥,假设出现了一对冲突的就叫有一个坏点,

那么,答案=没有冲突的-至少一个冲突的+至少两个冲突的...

出现了一个坏点,就认为是合并减少了一个数,

所以最后如果减少了k个数,就认为序列被拆成了n-k段,且每段内的数字相同

dp[i][j]表示前i个数被划分成了j段的方案数,

其中每段内的数字是相同的,也就是从[1,这一段的最小值]中取

1. 朴素转移即枚举最后一段在哪,补上这最后一段的贡献,对应了这一个区间的最小值

dp[i][j]+=\sum_{k=0}^{i-1}dp[k][j-1]*min_{x=k+1}^i{a_{x}}

复杂度O(n^3)

2. 注意到,第二维对容斥系数的贡献,只有第二维的奇偶性,所以可以改写为

dp[i][j\&1]+=\sum_{k=0}^{i-1}dp[k][j\&1\bigoplus 1]*min_{x=k+1}^i{a_{x}}

前缀和分别维护第二维为奇数/为偶数的和,

复杂度O(n^2)

3. 单调栈优化,注意到,如果找到了前面第一个小于ai的数是p,

那么,ai对前面的位置的数,也就是k<p的位置不再有贡献,

考虑k<p的位置的和,

\sum_{k=0}^{p-1}dp[k][j\&1\bigoplus 1]*min_{x=k+1}^i{a_{x}}

=\sum_{k=0}^{p-1}dp[k][j\&1\bigoplus 1]*min_{x=k+1}^p{a_{x}}

=dp[p][j\&1]

所以有,

dp[i][j\&1]+=dp[p][j\&1]+\sum_{k=p}^{i-1}dp[k][j\&1\bigoplus 1]*a[i]

那么,一边维护单调栈一边维护前缀和即可,

特别地,如果不存在这样的p,说明ai是前缀最小,

观察原式后代入ai,有dp[i][j\&1]+=\sum_{k=0}^{i-1}dp[k][j\&1\bigoplus 1]*a[i]

前0个数分成偶数段方案数为1,分成奇数段方案数为0,对应dp[0][0]=1

最后,答案=\sum至少出现偶数次冲突-\sum至少出现奇数次冲突方案

如果n为偶数,说明分成偶数段=出现偶数次冲突,答案是dp[n][0]-dp[n][1]

否则,如果n为奇数,说明分成奇数段=出现偶数次冲突,答案是dp[n][1]-dp[n][0]

复杂度O(n)

代码1

#include<iostream>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=5e5+10,mod=998244353;
int n,a[N],dp[N][2],sum[N][2],stk[N],c,ans;
// dp[i][j&1]+=dp[p][j&1]+sum dp[k][j&1^1]*a[i],p为小于ai的最大位置
void add(int &x,int y){x=(x+y)%mod;
}
int cal(int l,int r,int v){if(l==0)return sum[r][v];return (sum[r][v]-sum[l-1][v]+mod)%mod;
}
int main(){sci(n);dp[0][0]=sum[0][0]=1;rep(i,1,n){sci(a[i]);while(c && a[stk[c]]>=a[i]){c--;}rep(j,0,1){if(c)dp[i][j]=(dp[stk[c]][j]+1ll*cal(stk[c],i-1,j^1)*a[i]%mod)%mod;else dp[i][j]=1ll*cal(0,i-1,j^1)*a[i]%mod;sum[i][j]=(sum[i-1][j]+dp[i][j])%mod;}stk[++c]=i;//printf("i:%d dp:(%d,%d)\n",i,dp[i][0],dp[i][1]);}if(n&1)ans=(dp[n][1]-dp[n][0]+mod)%mod;else ans=(dp[n][0]-dp[n][1]+mod)%mod;printf("%d\n",ans);return 0;
}

代码2

自己的乱搞,基于以下这个式子,构造单调栈优化

其中,dp[i]表示以i结尾的方案数,枚举最后一段合并了几个数,

假设最后一段x个数,说明冲突了x-1次,容斥系数是(-1)的x-1次方,加上对应的贡献即可

dp[i]+=dp[j]*min_{x=j+1}^{i}a_{x}*(-1)^{i-j-1}, j<i

#include<iostream>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=5e5+10,mod=998244353;
//dp[i]+=dp[j]*min(a[j+1],...,a[i])*(-1)^(i-j-1),j<i
//dp[1]+=dp[0]*a[1]
//dp[2]+=dp[1]*a[2]
//dp[2]-=dp[0]*min(a[1],a[2])
int n,a[N],dp[N],sum[2],sum2[N],stk[N],mn[N],cp[N][2],c;
void add(int &x,int y){x=(x+y)%mod;
}
int main(){sci(n);rep(i,1,n){sci(a[i]);if(i==1)mn[i]=a[i];else mn[i]=min(mn[i-1],a[i]);}rep(i,0,n){while(c && a[stk[c]]>=a[i]){int p=stk[c],x=(p-1)&1;add(sum[x],mod-1ll*cp[p-1][0]*a[p]%mod);//jadd(sum2[x],cp[p-1][0]);//jadd(sum[x^1],mod-1ll*cp[p-1][1]*a[p]%mod);//jadd(sum2[x^1],cp[p-1][1]);//jc--;}//1 2 4 6 5 7//printf("i:%d sum:(%d,%d) sum2:(%d,%d)\n",i,sum[0],sum[1],sum2[0],sum2[1]);int v1=(sum[i&1^1]-sum[i&1]+mod)%mod;int v2=1ll*(sum2[i&1^1]-sum2[i&1]+mod)*a[i]%mod;dp[i]=(v1+v2)%mod;if(i&1)add(dp[i],mn[i]);//dp[0]的贡献else add(dp[i],mod-mn[i]);//printf("i:%d dp:%d\n",i,dp[i]);int x=(i-1)&1;stk[++c]=i;cp[i-1][0]=sum2[x];cp[i-1][1]=sum2[x^1];add(sum[x],1ll*cp[i-1][0]*a[i]%mod);add(sum[x^1],1ll*cp[i-1][1]*a[i]%mod);sum2[0]=sum2[1]=0;while(c && a[stk[c]]>=a[i+1]){int p=stk[c],x=(p-1)&1;add(sum[x],mod-1ll*cp[p-1][0]*a[p]%mod);//jadd(sum2[x],cp[p-1][0]);//jadd(sum[x^1],mod-1ll*cp[p-1][1]*a[p]%mod);//jadd(sum2[x^1],cp[p-1][1]);//jc--;}stk[++c]=i+1;x=i&1;cp[i][0]=(sum2[x]+dp[i])%mod;cp[i][1]=sum2[x^1];sum2[0]=sum2[1]=0;add(sum[x],1ll*cp[i][0]*a[i+1]%mod);add(sum[x^1],1ll*cp[i][1]*a[i+1]%mod);}printf("%d\n",dp[n]);return 0;
}

这篇关于AtCoder Regular Contest 115 E. LEQ and NEQ(容斥 单调栈优化dp)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/627923

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]