2021牛客暑期多校训练营1 I-Increasing Subsequence(期望dp+优化)

I-Increasing Subsequence

fi,j,0/1f_{i,j,0/1}fi,j,0/1表示上一轮第一个人选了iii,第二个人选了jjj,并且当前是第1/2个人选择的概率

转移考虑枚举k下一步往哪走
fi,k,1=∑fi,j,0/cntf_{i,k,1}=\sum f_{i,j,0}/ \text{cnt}fi,k,1=fi,j,0/cnt

fk,j,0=∑fi,j,1/cntf_{k,j,0}=\sum f_{i,j,1}/ \text{cnt}fk,j,0=fi,j,1/cnt

答案是所有数组之和:每进一个新状态都意味着游戏多进行了一局,因此把出现的概率全部累加就是期望局数。
显然的暴力~
时间复杂度:O(n3)O(n^3)O(n3)

Code1

#include<bits/stdc++.h>using namespace std;
using ll=long long;const int N=5010;
const int mod=998244353;ll f[N][N][2];
ll inv[N];
int a[N],n;
ll qmi(ll a,ll b)
{ll res=1;while(b){if(b&1) res=res*a%mod;a=a*a%mod;b>>=1;}return res;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) inv[i]=qmi(i,mod-2);for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++)for(int j=0;j<=n;j++){if(j==0) f[i][j][0]=inv[n];int ct=0;// 第一个人for(int k=j+1;k<=n;k++)if(a[k]>a[i]&&a[k]>a[j]) ct++;for(int k=j+1;k<=n;k++)if(a[k]>a[i]&&a[k]>a[j]) f[i][k][1]=(f[i][k][1]+inv[ct]*f[i][j][0])%mod;// 第二个人ct=0;for(int k=i+1;k<=n;k++)if(a[k]>a[i]&&a[k]>a[j]) ct++;for(int k=i+1;k<=n;k++)if(a[k]>a[i]&&a[k]>a[j]) f[k][j][0]=(f[k][j][0]+inv[ct]*f[i][j][1])%mod;}ll ans=0;for(int i=1;i<=n;i++)for(int j=0;j<=n;j++) ans+=f[i][j][0]+f[i][j][1],ans%=mod;cout<<ans<<'\n';return 0;
}

没听懂讲题人上述解法的优化,询问了mrk大佬的思路。并且参考下面题解,感觉更容易理解lalalzo题解

对于上述做法关键是我们需要区分这一步是谁走的,而下面的做法考虑在所给序列的值域上从小到大走,保证了所选必须比前面所有选的值要大,还有一个限制就是对于每一个人的所选序号单增。

设计dp

状态表示:fu,vf_{u,v}fu,v表示最后一个人选择vvv而倒数第二个人选择uuu,值域从小到大走需要满足u<vu<vu<v停下来的期望步数。

状态转移:考虑移动一步fu,v→fv,kf_{u,v}\to f_{v,k}fu,vfv,k需要满足u<v<kand posu<posku<v<k \text{ and } \text{pos}_u<\text{pos}_ku<v<k and posu<posk
fu,v=1cnt∑kfv,k+1f_{u,v}=\frac{1}{\text{cnt}}\sum_k f_{v,k}+1fu,v=cnt1kfv,k+1
发现满足posu<posk\text{pos}_u<\text{pos}_kposu<posk意味着每一个人的所选序号单增。非常巧妙啊~

于是有下面Code2常见的记忆化搜索写法(我之前比较熟悉记忆化搜索)

Code2

#include<bits/stdc++.h>
using namespace std;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=5010,mod=998244353;
using ll=long long;
int pos[N],n;
ll inv[N],f[N][N];
ll qmi(ll a,ll b)
{ll v=1;while(b){if(b&1) v=v*a%mod;a=a*a%mod;b>>=1;}return v;
}
// u<v<k
ll dfs(int u,int v)
{if(f[u][v]!=-1) return f[u][v];// f[u][v] -> f[v][k]  u<v<k&&pos[u]<pos[k]int ct=0;for(int k=v+1;k<=n;k++) if(pos[u]<pos[k]) ct++;f[u][v]=(ct>0?1:0);for(int k=v+1;k<=n;k++) if(pos[u]<pos[k]) f[u][v]=(f[u][v]+dfs(v,k)*inv[ct])%mod;return f[u][v];
}
int main()
{n=rd();for(int i=1;i<=n;i++) pos[rd()]=i;for(int i=1;i<=n;i++) inv[i]=qmi(i,mod-2);memset(f,-1,sizeof f);ll ans=0;for(int i=1;i<=n;i++) ans=(ans+dfs(0,i))%mod;ans=ans*inv[n]%mod;printf("%lld\n",ans);}

Code3

把上面记忆化搜索代码转化为迭代(考虑该状态会对哪些状态产生贡献),就有了下面的Code3

#include<bits/stdc++.h>
using namespace std;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=5010,mod=998244353;
using ll=long long;
int pos[N],n;
ll inv[N],f[N][N];
ll qmi(ll a,ll b)
{ll v=1;while(b){if(b&1) v=v*a%mod;a=a*a%mod;b>>=1;}return v;
}
int main()
{n=rd();for(int i=1;i<=n;i++) pos[rd()]=i;for(int i=1;i<=n;i++) inv[i]=qmi(i,mod-2);// f[k][i] -> f[i][j]  k<i<j && pos[k]<pos[j]// f[k][i] +=1/ct f[i][j] + 1for(int i=n;i>=1;i--){for(int k=0;k<i;k++){int ct=0;for(int j=i+1;j<=n;j++) if(pos[j]>pos[k]) ct++;for(int j=i+1;j<=n;j++)if(pos[j]>pos[k])f[k][i]=(f[k][i]+inv[ct]*f[i][j]%mod);if(ct) f[k][i]=(f[k][i]+1)%mod;}}ll ans=0;for(int i=1;i<=n;i++) ans=(ans+f[0][i])%mod;ans=ans*inv[n]%mod;printf("%lld\n",ans);}

Code4

不难发现每次我们想知道posk<posj\text{pos}_k<\text{pos}_jposk<posj,可以预处理前缀和优化。

#include<bits/stdc++.h>
using namespace std;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=5010,mod=998244353;
using ll=long long;
int pos[N],n;
ll sum[N],cnt[N],inv[N],f[N][N];
ll qmi(ll a,ll b)
{ll v=1;while(b){if(b&1) v=v*a%mod;a=a*a%mod;b>>=1;}return v;
}
int main()
{n=rd();for(int i=1;i<=n;i++) pos[rd()]=i;for(int i=1;i<=n;i++) inv[i]=qmi(i,mod-2);for(int i=n;i>=1;i--){memset(cnt,0,sizeof cnt);memset(sum,0,sizeof sum);for(int j=i+1;j<=n;j++){cnt[pos[j]]++;sum[pos[j]]=f[i][j];}for(int j=n-1;j>=0;j--){cnt[j]+=cnt[j+1];sum[j]=(sum[j]+sum[j+1])%mod;}for(int k=0;k<i;k++) {int id=pos[k];f[k][i]=(sum[id]*inv[cnt[id]]%mod+1)%mod;}}ll ans=0;for(int i=1;i<=n;i++) ans=(ans+f[0][i])%mod;ans=ans*inv[n]%mod;printf("%lld\n",ans);
}

总结:

其实对于上述两种方法有本质的区别就是定义的状态一个是概率一个是期望步数,需要注意如何定义状态~

要加油哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/318850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客题霸 [子数组的最大累加和问题] C++题解/答案

牛客题霸 [子数组的最大累加和问题] C题解/答案 题目描述 给定一个数组arr&#xff0c;返回子数组的最大累加和 例如&#xff0c;arr [1, -2, 3, 5, -2, 6, -1]&#xff0c;所有子数组中&#xff0c;[3, 5, -2, 6]可以累加出最大的和12&#xff0c;所以返回12. [要求] 时间复…

无限序列 (ybtoj C.3)

解析 乍一看很蒙的题 首先&#xff0c;a-b1的个数可以等价于**&#xff08;1-b&#xff09;1的个数减去&#xff08;1-a-1&#xff09;1的个数** 分析之后发现&#xff0c;经过多次变换后&#xff1a; 长度 1的个数 1 1 2 1 3 2 5 3 8 5 … … 又是熟悉的斐波拉契。。。 但是我…

P5644-[PKUWC2018]猎人杀【NTT,分治】

正题 题目链接:https://www.luogu.com.cn/problem/P5644 题目大意 nnn个人&#xff0c;每个人被选中的权重是aia_iai​。每次按照权重选择一个没有死掉的人杀死&#xff0c;求第111个人最后死的概率。输出答案对998244353998244353998244353取模。 wi>0,∑i1nwi≤105w_i>…

对传统应用进行容器化改造

本文由 陈计节 翻译自 FP Complete 网站上的文章 CONTAINERIZING A LEGACY APPLICATION: AN OVERVIEW&#xff0c;原作者 Emanuel Borsboom。以下为译文全文&#xff0c;如需阅读英文原文&#xff0c;请转到文末获取链接&#xff1a;本文接下来简要介绍什么是容器化&#xff0c…

2021牛客暑期多校训练营2 L-WeChat Walk(分块)

L-WeChat Walk 每个大点记录一下邻接点的最大步数 每次修改的时候&#xff0c;枚举修改点的邻接的大点来更新 修改大点的时候直接判是不是比邻接点都大 代码抄的std好不容易才看懂~ Code1 #include<bits/stdc.h> using namespace std; template <class Tint> T…

牛客题霸 [求平方根] C++题解/答案

牛客题霸 [求平方根] C题解/答案 题目描述 实现函数 int sqrt(int x). 计算并返回x的平方根 题解&#xff1a; 要求返回平方根&#xff0c;我们就找一个i&#xff0c;使得ii<x&&(i1)(i1)>x 这样的i就是我们要找的答案 注意&#xff0c;x有可能为负数&#xf…

二分算法:平均值(洛谷 UVA1451)

解析 这道题寻找平均值的max&#xff0c;答案明显具有单调性&#xff0c;所以采用二分算法 从0到1不断取中点mid作为平均值的可能点&#xff0c;看是否存在不短于l的数列均值&#xff1e;mid不难得到以下代码&#xff1a; double st0,ed1;for(int i1;i<10;i){double mid(s…

P5287-[HNOI2019]JOJO【KMP】

正题 题目链接:https://www.luogu.com.cn/problem/P5287 题目大意 开始一个空串&#xff0c;nnn个操作 在末尾加入xxx个ccc字符&#xff08;保证和ccc和前面的字符不同&#xff09;返回到第xxx次操作之后 每次操作完成后求所有前缀的最长的borderborderborder长度和 1≤n≤…

牛客题霸 [数组中只出现一次的数字] C++题解/答案

牛客题霸 [数组中只出现一次的数字] C题解/答案 题目描述 一个整型数组里除了两个数字之外&#xff0c;其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 题解&#xff1a; 用map来记录每个数字出现几次&#xff0c;然后再循环一遍看哪个数字出现一次&#x…

Asp.NetCore依赖注入和管道方式的异常处理及日志记录

前言在业务系统&#xff0c;异常处理是所有开发人员必须面对的问题&#xff0c;在一定程度上&#xff0c;异常处理的能力反映出开发者对业务的驾驭水平&#xff1b;本章将着重介绍如何在 WebApi 程序中对异常进行捕获&#xff0c;然后利用 Nlog 组件进行记录&#xff1b;同时&a…

2021“MINIEYE杯”中国大学生算法设计超级联赛(2)I love counting(Trie树)

I love counting O{Mlog⁡aMAX(BN/B)}O\{M\log{a_{\text{MAX}}}(\text{BN/B})\}O{MlogaMAX​(BN/B)} md考场写的莫队Trie一直T #include<bits/stdc.h> using namespace std; using lllong long; using piipair<int,int>; using plipair<ll,int>; constexpr …

字符串:凯撒密码(洛谷P1914)

解析 只需将每一位ascll码加n即可 但要注意的是c的ascll码是有上限的 我一开始是这么写的&#xff1a; for(int i1;i<l;i){s[i] n;while(s[i]>z) s[i] - 26;printf("%c",s[i]);}结果&#xff1a; (真的是随便打的&#xff0c;不巧有些攻击性。。&#xff0…

P6113-[模板]一般图最大匹配【带花树】

正题 题目链接:https://www.luogu.com.cn/problem/P6113 题目大意 给出一张无向图&#xff0c;求最大匹配。 1≤n≤103,1≤m≤51041\leq n\leq 10^3,1\leq m\leq 5\times 10^41≤n≤103,1≤m≤5104 解题思路 带花树的模板&#xff0c;我也不会讲/kel 所以看下面两篇大佬的博…

牛客题霸 [跳台阶] C++题解/答案

牛客题霸 [跳台阶] C题解/答案 题目描述 一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法&#xff08;先后次序不同算不同的结果&#xff09;。 题解&#xff1a; 递归的入门题 如果只剩一个台阶&#xff0c;只有一种跳…

.NET Core实战项目之CMS 第四章 入门篇-Git的快速入门及实战演练

写在前面上篇文章.NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入我带着大家通过分析了一遍ASP.NET Core的源码了解了它的启动过程&#xff0c;然后又带着大家熟悉了一遍配置文件的加载方式&#xff0c;最后引出了依赖注入以及控制反转的概念&#xff01;如果…

高精度:麦森数*(洛谷P1045)

P1045 [NOIP2003 普及组] 麦森数 解析 看似只是正常的一个高精 然而 暗藏杀机 一开始随手那么一写 。。。 (即使用了快速幂)时间复杂度过于感人 后来我们发现&#xff1a; 第一问位数的计算不必真的算出来&#xff0c;只需把2的p次幂转化为10的k次幂即可&#xff08;具体请…

2021“MINIEYE杯”中国大学生算法设计超级联赛(2)I love exam(背包)

I love exam 不知道为啥刚开始不写&#xff0c;那么简单的背包预处理dp&#xff0c;太菜了吧 fi,jf_{i,j}fi,j​对于第i门课来说花费j天得到的最大分数 gi,j,pg_{i,j,p}gi,j,p​考虑前i门课&#xff0c;花费j天复习得到的最大分数 #include<bits/stdc.h> using namespa…

P2012-拯救世界2【EGF】

正题 题目链接:https://www.luogu.com.cn/problem/P2012 题目大意 121212种东西排列成长度为nnn的序列&#xff0c;要求前四种出现奇数次&#xff0c;后四种出现偶数次&#xff0c;求方案。TTT组数据&#xff0c;对10910^9109取模。 1≤n<263,1≤T≤21051\leq n< 2^{63}…

彼之蜜糖,吾之砒霜——聊聊软件开发中的最佳实践

“描述一个事物&#xff0c;唯有一个名词定义它的概念&#xff0c;唯有一个动词揭露它的行为&#xff0c;唯有一个形容词表现它的特征。要做的&#xff0c;就是用心去寻找那个名词、那个动词、那个形容词……”—— 福楼拜 (Gustave Flaubert)我想讲个故事。很久很久以前&#…

贪心: Array Splitting(数列分段)(洛谷CF1175D)

解析 这题可以转化一下&#xff1a; &#xff08;《神笔马良》。。。。&#xff09; 计算这些长方形对应下标的总加和 我们可以一层一层往上垒,假设第i层起始点为xi&#xff0c;总和为sumi&#xff0c;再设从1到i的前缀和为si 显然第一层x11&#xff0c;sum1sn 对于第二层x2&…