wave

題意:求有多少個1~n的排列知足:ide

其中n<=50spa

解:3d

賊神的一道題。code

如何處理絕對值?blog

從小到大按順序放數,能夠拆掉絕對值。it

若是你放的旁邊有個空隙,那麼貢獻-i,若是旁邊有個數,貢獻+iio

而後你設的是f[i][j][k][s]表示前i個數,有j+1段數(j個間隔),兩端點狀態爲k(0~2分別表示都沒有放數,放了一個,放了兩個),目前貢獻爲s的方案數。event

因此要求的就是f[n][0][2][m]class

考慮轉移:咱們能夠把i放在某個間隔裏,三種狀況。還能夠放在兩端,4種狀況。cli

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 typedef long long LL;
 5 const int N = 53, MO = 1e9 + 7, D = 1500;
 6 
 7 int n;
 8 LL f[2][N][3][3000];
 9 
10 inline void add(LL &a, const LL &b) {
11     a += b;
12     while(a >= MO) {
13         a -= MO;
14     }
15     while(a < 0) {
16         a += MO;
17     }
18     return;
19 }
20 
21 int main() {
22 
23     freopen("wave.in", "r", stdin);
24     freopen("wave.out", "w", stdout);
25     int m;
26     scanf("%d%d", &n, &m);
27     
28     LL ans = 0;
29     
30     f[1][0][0][D - 2] = 1; // 1 in mid
31     f[1][0][1][D - 1] = 2; // 1 in edge
32     for(int i = 1; i < n; i++) {
33         for(int j = 0; j <= (n + 1) >> 1; j++) {
34             for(int k = 0; k < 3; k++) {
35                 for(int s = 0; s < 3000; s++) {
36                     if(!f[i & 1][j][k][s]) {
37                         continue;
38                     }
39                     LL t = f[i & 1][j][k][s];
40                     //printf("%d %d %d %d = %lld\n", i, j, k, s - D, t);
41                     // f[i][j][k][s] -> ?
42                     if(k < 2) { // i+1 in edge
43                         add(f[(i + 1) & 1][j][k + 1][s + i + 1], t * (2 - k)); // merge
44                         add(f[(i + 1) & 1][j][k][s], t * (2 - k));             // edge 
45                         add(f[(i + 1) & 1][j + 1][k + 1][s - i - 1], t * (2 - k)); // end
46                         if(j + (2 - k) + i < n - 1) {
47                             add(f[(i + 1) & 1][j + 1][k][s - ((i + 1) << 1)], t * (2 - k)); // split
48                             //printf("%d %d %d %d %d \n", i + 1, j + 1, k, s - ((i + 1) << 1) - D, f[i + 1][j + 1][k][s - ((i + 1) << 1)]);
49                         }
50                     }
51                     // i+1 in mid
52                     if(j) {
53                         add(f[(i + 1) & 1][j][k][s], t * j * 2); // edge
54                         add(f[(i + 1) & 1][j - 1][k][s + ((i + 1) << 1)], t * j); // merge
55                         if(j + (2 - k) + i < n - 1) { // split
56                             add(f[(i + 1) & 1][j + 1][k][s - ((i + 1) << 1)], t * j);
57                         }
58                     }
59                     f[i & 1][j][k][s] = 0;
60                 }
61             }
62         }
63     }
64     
65     printf("%lld", f[n & 1][0][2][m + D]);
66     return 0;
67 }
AC代碼

空間不夠,要滾動。而後每次要清零。當前價值可能爲負因此要加一個偏移量。

相同的套路還有相鄰的兩個數貢獻爲max/min,幾乎如出一轍。

相關文章
相關標籤/搜索