[SDOI2010]地精部落

Linkerc++

作法:DP+組合數spa

首先咱們定義\(dp[i]\)爲長度爲i時的方案數。debug

不難想到,若是要知足每個點都知足爲山峯或者山谷的話,確定是曲折的。(及若山谷爲0,山峯爲1,確定是\(010101010101\cdots\) 或者 \(101010101010\cdots\)code

咱們把這一個長度爲n分紅兩個部分,設左邊的長度爲j,則右邊的長度爲i-jget

發現這樣不太好轉移,因而咱們把dp的狀態再定義嚴格一點it

咱們定義\(dp[i]\)表示長度爲i,且第一個嚴格爲山峯的方案書。class

那麼咱們此時此刻,左邊的開頭與右邊的開頭就都是山峯了。bug

由上面的\(01\)串可得,咱們的左邊最後一個也得是山峯,也就是說,咱們左邊的長度得是奇數static

最後一個問題:如何保證左邊最後一個右邊第一個必定爲山峯?di

咱們把中間放上了一個最小值不就行了嗎?

所以咱們獲得dp轉移方程

\(dp[i] = \sum_{j=1}^{i-1} dp[j] * dp[i-1-j]*C_{i-1}^{j}(j\%2==1)\)

而後進行dp就行了。

對於山谷,與山峯的狀況是徹底同樣的,所以咱們直接*2便可

Code:

#include<bits/stdc++.h>
#define re register
#define rep(i,a,b) for(re int i=a,i##end=b; i<=i##end; i++)
#define drep(i,a,b) for(re int i=a,i##end=b; i>=i##end; i--)
#define repp(i,a,b) for(re int i=a,i##end=b; i<i##end; i++)
#define drepp(i,a,b) for(re int i=a,i##end=b; i>i##end; i--)
#define Erep(i,x) for(re int i=head[x]; i; i=Edge[i].nxt)
#define lowbit(x) ((x)&-(x))
#define ms(x,a) memset(x,a,sizeof x)
#define debug(x) cerr<<#x<<" = "<<x<<endl
#define CM cerr<<(&S2-&S1)/1024./1024.<<"MB"<<endl
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define fi first
#define se second
#define coint const int
#define coll const ll
typedef long long ll;
using namespace std;
template<class T>inline T rd(){
    static char ch;static bool neg;static T x;
    for(neg=0, ch=0; ch>'9'||ch<'0'; neg|=(ch=='-'),ch=getchar());
    for(x=0; ch<='9'&&ch>='0'; x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar());
    return neg?-x:x;
}
template<class T>inline T Max(const T &x, const T &y) { return x>y?x:y; }
template<class T>inline T Min(const T &x, const T &y) { return x<y?x:y; }

bool S1;

int n,p;

struct P100{
    static coint N=4200+5;
    int dp[N]; // dp[i] -> 長度爲i,符合條件 且 嚴格定義第一個爲山峯(山谷同理)的方案數
    int C[N][N];
    inline void Upd(int &x, coint y){
        x+=y; x-=(x>=p?p:0); return;
    }
    inline void solve(){
        rep(i,0,N-5){
            C[i][i]=C[i][0]=1;
            repp(j,1,i) Upd(C[i][j],C[i-1][j]+C[i-1][j-1]);
        }
        dp[0]=dp[1]=1;
        rep(i,2,n){
            repp(j,1,i) if(j&1){ // 保證中間'1'的兩邊都是山峯
                dp[i]=(dp[i]+1ll*dp[j]*dp[i-1-j]%p*C[i-1][j]%p)%p;
            }
        }
        Upd(dp[n],dp[n]);
        printf("%d\n",dp[n]);
        return;
    }
}p100;

bool S2;

int main(){
//  CM;
//  freopen("goblin.in","r",stdin);
//  freopen("goblin.out","w",stdout);
    n=rd<int>(),p=rd<int>();
    if(n<=10) return p20.solve(),0;
    if(n<=18) return p40.solve(),0;
    p100.solve();
//  fclose(stdin); fclose(stdout);
    return 0;
}
相關文章
相關標籤/搜索