bzoj 1925
一道略有难度的dp
设状态dp[i][j]表示长度为i,开头高度为j且为山峰的方案数
考虑到一个序列是对称的,所以总方案数即为2*\sum_{i=2}^{n}dp[n][i]
这样我们只需考虑转移即可
首先,我们发现,如果两个数i与i+1不相邻,那么交换这两个数之后方案数不变
这个...感性理解一下,如果两个数不相邻,若i在原位置是山峰,换成i+1后肯定仍然是山峰,而如果i在原位置是山谷,那么换成i+1后由于i+1并不在i的左右,因此换成i+1后肯定仍为山谷
剩下的同理可证
因此第一个方向就是:假设j与j-1不相邻,那么转移就是dp[i][j]=dp[i][j-1](即原来j-1在山峰,然后交换j与j-1,方案数不变)
那么如果j与j-1相邻呢?
那么需要第二个性质:如果一个序列{a_{i}}是波动序列,那么序列{(n+1)-a_{i}}也是波动序列,且波峰波谷恰好相反
那么如果j与j-1相邻,j在首位为波峰,那么j-1必然在第二位为波谷,那么我们只需求出j-1为波谷的方案即可
再根据第二条性质,我们可以将所有以j-1开头为波谷的序列每一位用i-1+1去减,那么就会变成以i-j+1为开头波峰的方案数
因此总转移方程即为dp[i][j]=dp[i][j-1]+dp[i-1][i-j+1]
滚动数组优化一下即可
#include <cstdio> #define ll long long ll dp[2][4505]; ll n,p; int main() { scanf("%lld%lld",&n,&p); dp[0][2]=1; for(int i=3;i<=n;i++)for(int j=2;j<=i;j++)dp[i&1][j]=(dp[i&1][j-1]+dp[(i-1)&1][i-j+1])%p; ll ans=0; for(int i=2;i<=n;i++)ans=(ans+dp[n&1][i])%p; printf("%lld\n",ans*2%p); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步