1. 程式人生 > >BZOJ1925 [SDOI2010]地精部落

BZOJ1925 [SDOI2010]地精部落

Description

傳送門
\(N\)的排列中有多少個波動數列.

波動數列是指對於數列中的每一個數,他兩邊的數必須嚴格小於或大於自己.

\[ n \leq 4200, Mod \leq 1e9 \]

Solution

首先我們必須要搞清楚3個性質

  • First: 在一個波動數列中,若兩個 i 與 i+1 不相鄰,那麼我們直接交換這兩個數字就可以組成一個新的波動數列; 舉個栗子: 5 2 3 1 4

4 2 3 1 5

  • Second: 把波動數列中的每個數字Ai 變成 (N+1)-Ai 會得到另一個波動數列,且新數列的山峰與山谷情況相反;

舉個栗子: 1 4 2 5 3 (用 6 - 每個數) 1是山谷,4是山峰,後面類推

5 2 4 1 3 這個數列也是波動的 ,且 5是山峰,2是山谷;

  • Third: 波動序列有對稱性。 栗子:1 4 2 5 3 to 3 5 2 1 4

(以上轉自luogu)

那麼我們設\(Dp[i][j]\)表示\(1\)\(i\)的排列中, 當前數列首端為高度j的山峰的方案數.

考慮如果j,j - 1不相鄰, 那麼直接交換就可以.即\(Dp[i][j - 1]\)

如果相鄰, 那麼顯然j - 1是山谷. 所以應用性質二,把山峰變成山谷,即:\(DP[i - 1][(i - 1 + 1) - (j - 1)] = DP[i - 1][i - j + 1]\)

所以答案為:\(DP[i][j] = DP[i][j - 1] + DP[i - 1][i - j + 1]\)

由於空間太小, 考慮滾動陣列優化.

Codes

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(s) debug("The massage in line %d, Function %s: %s\n", __LINE__, __FUNCTION__, s)
typedef long long LL;
typedef long double LD;
const int BUF_SIZE = (int)1e6 + 10;
struct fastIO {
    char buf[BUF_SIZE], buf1[BUF_SIZE];
    int cur, cur1;
    FILE *in, *out;
    fastIO() {
        cur = BUF_SIZE, in = stdin, out = stdout;
        cur1 = 0;
    }
    inline char getchar() {
        if(cur == BUF_SIZE) fread(buf, BUF_SIZE, 1, in), cur = 0;
        return *(buf + (cur++));
    }
    inline void putchar(char ch) {
        *(buf1 + (cur1++)) = ch;
        if (cur1 == BUF_SIZE) fwrite(buf1, BUF_SIZE, 1, out), cur1 = 0;
    }
    inline int flush() {
        if (cur1 > 0) fwrite(buf1, cur1, 1, out);
        return cur1 = 0;
    }
}IO;
#define getchar IO.getchar
#define putchar IO.putchar
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for(;!isdigit(ch); ch = getchar()) if(ch == '-') flag *= -1;
    for(;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(int x) {
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}
void putString(char s[], char EndChar = '\n') {
    rep(i, 0, strlen(s) - 1) putchar(*(s + i));
    if(~EndChar) putchar(EndChar);
}

#define Maxn 4209
int n, p, dp[2][Maxn];
namespace INIT {
    void Main() {
        n = read(), p = read();
    }
}
namespace SOLVE {
    void Main() {
        dp[1][1] = 1;
        rep(i, 2, n) 
            rep(j, 1, i) 
                dp[i & 1][j] = (dp[(i - 1) & 1][i - j + 1] + dp[i & 1][j - 1]) % p;
        int ans = 0;
        rep(i, 1, n) ans = (ans + dp[n & 1][i]) % p;
        ans = ans * 2 % p;
        write(ans), putchar('\n');
    }
}
int main() {
#ifdef Qrsikno
    freopen("BZOJ1925.in", "r", stdin);
    freopen("BZOJ1925.out", "w", stdout);
#endif
    INIT :: Main();
    SOLVE :: Main();
#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return IO.flush();
}