#dp#洛谷 2467 JZOJ 1582 地精部落

題目

問有多少個1n1\sim n的排列對於i\forall i1<i<n1<i<n的情況下都滿足a[i1]<a[i]>a[i+1]a[i-1]<a[i]>a[i+1]a[i1]>a[i]<a[i+1]a[i-1]>a[i]<a[i+1]


分析

思維好題
首先考慮性質,

  • 對於不相鄰的兩個數j,j+1j,j+1,可以互換
  • 合法的排列翻轉依舊合法
  • 把合法排列每個數jj都變成nj+1n-j+1,排列依舊合法

考慮dp,設dp[i][j]dp[i][j]表示選擇1i1\sim i,第一個數爲jj,且第一個數大於第二個數的方案,所以最後答案要乘2
因爲jjj1j-1在不相鄰時可以互換,所以dp[i][j]dp[i][j]可以由dp[i][j1]dp[i][j-1]轉移而來,接着如果jjj1j-1相鄰,那麼也就轉換成選擇1i11\sim i-1,第一個數爲j1j-1,且第一個數小於第二個數的方案,但是這和定義不符,考慮轉換成選擇1i11\sim i-1,第一個數爲ij+1i-j+1,且第一個數大於第二個數的方案,因爲如果進行第三條性質,那麼原來大的反而小,初始化dp[2][2]=1dp[2][2]=1
綜上所述,dp[i][j]=dp[i][j1]+dp[i1][ij+1]dp[i][j]=dp[i][j-1]+dp[i-1][i-j+1]


代碼

#include <cstdio>
#define rr register
using namespace std;
int n,mod,dp[2][4211],ans;
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
signed main(){
	scanf("%d%d",&n,&mod),dp[0][2]=1;
	for (rr int i=3;i<=n;++i)
	for (rr int j=2;j<=i;++j)
	    dp[i&1][j]=mo(dp[i&1][j-1],dp[(i&1)^1][i-j+1]);
	for (rr int i=2;i<=n;++i) ans=mo(ans,dp[n&1][i]);
	return !printf("%d",mo(ans,ans));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章