解決
これは、ああについて話していないようです
セット\は(F [i]は[J ] \) 表現\(I \) 、山
最後に、山の高さをされて到達\(私は\)最初用のシート\(j個\)大は、
そして、最後の山の谷。
注、\は(私は\)されている代表\(私は\)山があり、必ずしも高さを意味するものではありません\(1 \)を〜\(私は\します)。
\(j個\)は、離散事に似ています。
私たちは、その後、設定することを検討\(G [i]の[J] \) 、
以外の最後の山のピークに加えて、その定義と\(F [I] [J ] \) 同じ。
そこ式\(F [I] [J] = \ sum_ {J} = K-I 1} ^ {Gの[1-I] [K] \)。
ここでの最大値と最小値の問題について話をします、
最大値は簡単に大に取得することができ、
しかし、考える前に\を(私は\)山の一定割合の最後の前に\(I-1 \)小(谷のように)シート、
だから、最後のものは初めてです\(j個\)それはフロント前であることは大きな\(I-1 \)ランキングで、山よりも小さくすることはできませんです\(j個\) 。
(誘導はそれを理解して、少し明確に発現してもよいです。)
1つの明白なことがあり、
正当なプログラムがある場合は、
そして、私たちはそれぞれの山の高さに置く\(H_I \を)に(N-H_i- + 1 \)は\、プログラムがまだ合法である(つまり、谷に山)。
したがって\(G [I] [J] = F [I] [I-J + 1] \)。
合わせたビットになる\(のF [I] [J] = \ sum_。1} ^ {K = {F} IJ [1-I] [K] \)。
そして、これは接尾辞やものに相当します。
従ってDPは、式で\が(F [I] [J] = F [I]、[J + 1] + Fの[1-I〕〔のIJ] \)。
回転式配列の下のライン上のスペースの最適化。
#include <iostream>
#include <cstdio>
#include <cstring>
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
using namespace std;
inline int read(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return f*sum;
}
const int N=4301;
int n,Mod,f[2][N];
int ans=0;
int main(){
n=read();Mod=read();
f[1][1]=1;int now=1;
for(int i=2;i<=n;i++){
now^=1;memset(f[now],0,sizeof(f[now]));
for(int j=i-1;j;j--) f[now][j]=(f[now][j+1]+f[now^1][i-j])%Mod;
}
for(int i=1;i<=n;i++) ans=(ans+f[now][i])%Mod;
printf("%d\n",(ans<<1)%Mod);
return 0;
}