• wave


    题意:求有多少个1~n的排列满足:

    其中n<=50

    解:

    贼神的一道题。

    如何处理绝对值?

    从小到大按顺序放数,可以拆掉绝对值。

    如果你放的旁边有个空隙,那么贡献-i,如果旁边有个数,贡献+i

    然后你设的是f[i][j][k][s]表示前i个数,有j+1段数(j个间隔),两端点状态为k(0~2分别表示都没有放数,放了一个,放了两个),目前贡献为s的方案数。

    所以要求的就是f[n][0][2][m]

    考虑转移:我们可以把i放在某个间隔里,三种情况。还可以放在两端,4种情况。

     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
    ", 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 
    ", 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,几乎一模一样。

  • 相关阅读:
    GIT 相关
    createFile
    值传递、指针传递、引用传递
    Sightseeing trip
    find the longest of the shortest
    Laurenty and Shop
    Dima and Lisa
    Marina and Vasya
    Kolya and Tanya
    Two Substrings
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/9844713.html
Copyright © 2020-2023  润新知