bzoj2817 [ZJOI2012]波浪
Description
http://www.lydsy.com/JudgeOnline/upload/zjoi2012.pdf
正解:$dp$。
这道题好难啊。。
首先这道题里面有一个绝对值,这个很不好搞,我们考虑如何去掉。
我们只要按照从小到大的顺序插入所有数就行了。
那么插入一个数有很多种情况。
1.插入以后它两边都没有数。
2.插入以后它两边都有数。
3.插入以后它的一边有数。
4.插入在边界上,且它旁边没有数。
5.插入在边界上,且它旁边有数。
这$5$种情况对应$5$个不同的转移,于是我们可以设一个状态来表示这些转移。
设$f[i][j][k][l]$表示插入$i$个数,当前序列波动值为$j$,序列现在被分为$k$个连续的段,序列边界的状态为$l$,$l$为$0$表示边界没有数,为$1$表示边界有一个数,为$2$表示边界有两个数。
那么我们可以分几种情况转移,对应转移直接在代码里上注释吧,注意当$l$不同的时候转移系数也不同。
1 #include <bits/stdc++.h> 2 #define RG register 3 #define ll long long 4 #define M (4500) 5 6 using namespace std; 7 8 int n,m,k; 9 10 namespace db{ double f[2][9001][101][3]; } 11 namespace f128{ __float128 f[2][9001][101][3]; } 12 13 template<class T> 14 15 inline void pi(T ans,RG int k){ 16 printf("%d.",(int)ans); 17 while (k--){ 18 ans=(ans-(int)ans)*10; 19 if (!k) ans+=0.5; 20 printf("%d",(int)ans); 21 } 22 return; 23 } 24 25 template<class T> 26 27 inline void solve(T f[][9001][101][3]){ 28 f[1][M-2][1][0]=1,f[1][M-1][1][1]=2,f[0][M][1][2]=1; 29 for (RG int i=2,cur=0,pre=1;i<=n;++i,pre=cur,cur^=1){ 30 memset(f[cur],0,sizeof(f[cur])); 31 for (RG int j=0;j<=2*M;++j) 32 for (RG int k=1;k<=n-1;++k) 33 for (RG int l=0;l<=2;++l){ 34 if (!f[pre][j][k][l]) continue; 35 if (j+2*i<=2*M) f[cur][j+2*i][k-1][l]+=f[pre][j][k][l]*(k-1); //插入以后两边都有数 36 if (j>=2*i) f[cur][j-2*i][k+1][l]+=f[pre][j][k][l]*(k+1-l); //插入以后两边都没有数 37 f[cur][j][k][l]+=f[pre][j][k][l]*(k*2-l); //插入以后一边有数 38 if (l<2){ 39 if (j+i<=2*M) f[cur][j+i][k][l+1]+=f[pre][j][k][l]*(2-l); //插入在边界上,且旁边有数 40 if (j>=i) f[cur][j-i][k+1][l+1]+=f[pre][j][k][l]*(2-l); //插入在边界上,且旁边没有数 41 } 42 } 43 } 44 T ans=0; for (RG int i=M+m;i<=2*M;++i) ans+=f[n&1][i][1][2]; 45 for (RG int i=2;i<=n;++i) ans/=i; pi(ans,k); return; 46 } 47 48 int main(){ 49 #ifndef ONLINE_JUDGE 50 freopen("wave.in","r",stdin); 51 freopen("wave.out","w",stdout); 52 #endif 53 cin>>n>>m>>k; 54 if (k<=8) solve(db::f); 55 else solve(f128::f); 56 return 0; 57 }