題意:求有多少個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 }
空間不夠,要滾動。而後每次要清零。當前價值可能爲負因此要加一個偏移量。
相同的套路還有相鄰的兩個數貢獻爲max/min,幾乎如出一轍。