题目链接:https://vjudge.net/problem/HYSBZ-1925
数据范围:3≤N≤4200,P≤1e9
...网上都是大神,各种看不懂,DP神学啊,状态转移方程真难想TAT......
2019.11.23头晕,看清了解释,不明白为啥这样...
题意: 问你有多少长度为n的数列(1~n),它当中每个数字要么比旁边两个数字都小,要么比旁边两个数字都大。
前言:这种被称为抖动序列,抖动序列有两个性质:
(1)若一个抖动排列中x与x+1不相邻,那么交换x与x+1,依旧是抖动排列----所以 j与j-1不相邻的情况下,dp[i][j]=dp[i][j-1]
(2)一个[1,X]的抖动序列一定可以对应到[Y−X+1,Y]的一个抖动序列, 就相当于所有元素都取补数
---dp[i][j]=dp[i-1][j-1]=dp[i-1][(i-1)-(j-1)+1]=dp[i-1][i-j+1]
转移方程:dp[i][j]=dp[i][j-1]+dp[i-1][j-i+1]
dp[i][j]表示序列有i个数(注明:i个数为1到i),末尾的数字为j的序列种类数目。
另外我们这里采用滚动数组,只用dp[2][N]表示dp[i][j]
dp[i][j]与dp[i-1][j]其实可以看作这一行与上一行的关系,存的话就是x与x&1的关系。一个值为1,一个值为0
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 4210
int dp[2][maxn];
int main(){
int N,P,x=1;
cin>>N>>P;
dp[1][1]=1;
for(int i=2;i<=N;i++){
x^=1;
for(int j=1;j<=i;j++){
dp[x][j]=(dp[x^1][i-j+1]%P+dp[x][j-1]%P)%P;
}
}
long long ans=0;
for(int j=1;j<=N;j++){
ans=(ans+dp[x][j])%P;
}
cout<<ans*2%P<<endl;
}