• [SDOI2010]地精部落


    题目传送门

    这道题是一道$DP$题,思维难度比较大。

    题意:先定义波形数组:满足当$i$全为奇数或偶数时,$a[i]>a[i-1]$且$a[i]>a[i+1]$。

    求$n$的全排列中有多少个符合波形数组。

    想法是:如果第$i$个数是山峰,下一个肯定是山谷,也就是说要从剩下的且比第$i$个数小的数中挑到第$i+1$个数中。反之要挑大的数。

    我们记录状态为$f[i][j][0/1]$,$i$为剩下$i$个数,$j$表示有$j-1$个数小于刚刚选择的数,当第$3$个下标为$0$时,表示这是山谷,为$1$则说明是山顶。

    推理知道$f[i][j][0]$能影响$f[i-1][j..i][1]$,$f[i][j][1]$能影响$f[i-1][1..j-1][0]$。

    初始化为$f[n][0][0]=f[n][n+1][1]=1$,其他为$0$。原因在于:刚开始所有数都可以作为第一个数为山峰或山谷,要么没有数比上一层大,要么所有数比上一层都大。

    最后求$f[0][1][0]+f[0][1][1]$的值即可。

    此时时间复杂度为$O(n^3)$,空间为$O(n^3)$。

    但是过不了。。

    考虑将转移方程变化下,把刷表改成填表,就发现$f[i][j][1]=sum_{x=0}^jf[i+1][x][0]$,$f[i][j][0]=sum_{x=j+2}^{i+2}f[i+1][x][1]$,然后又发现可以在填表的过程中顺便求下$Sigma$,于是就优化到了$O(n^2)$。

    而空间可以通过滚动数组优化掉一维。

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 #define re register
     6 #define rep(i, a, b) for (re int i = a; i <= b; ++i)
     7 #define repd(i, a, b) for (re int i = a; i >= b; --i)
     8 #define maxx(a, b) a = max(a, b);
     9 #define minn(a, b) a = min(a, b);
    10 #define LL long long
    11 #define inf (1 << 30)
    12 
    13 inline int read() {
    14     int w = 0, f = 1; char c = getchar();
    15     while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar();
    16     while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar();
    17     return w * f;
    18 }
    19 
    20 const int maxn = 4200 + 10;
    21 
    22 int f[2][maxn][2], n, P, cur = 1;
    23 
    24 int main() {
    25     n = read(), P = read();
    26 
    27     memset(f, 0, sizeof(f));
    28 
    29     f[cur][0][0] = f[cur][n+1][1] = 1;
    30 
    31     repd(i, n-1, 0) {
    32         cur ^= 1;
    33         rep(j, 0, i+2) f[cur][j][0] = f[cur][j][1] = 0;
    34         int v = f[cur^1][0][0];
    35         rep(j, 1, i+1) {
    36             v = (v + f[cur^1][j][0]) % P;
    37             f[cur][j][1] = (f[cur][j][1] + v) % P;
    38         }
    39         v = f[cur^1][i+2][1];
    40         repd(j, i+1, 1) {
    41             f[cur][j][0] = (f[cur][j][0] + v) % P;
    42             v = (v + f[cur^1][j][1]) % P;
    43         }
    44     }
    45 
    46     printf("%d", (f[cur][1][0] + f[cur][1][1]) % P);
    47 
    48     return 0;
    49 }

    这道题还可以用数学统计下手推方程。

  • 相关阅读:
    LINQ to SQL语句(9)之Top/Bottom和Paging和SqlMethods
    LINQ to SQL语句(8)之Concat/Union/Intersect/Except
    LINQ to SQL语句(7)之Exists/In/Any/All/Contains
    LINQ to SQL语句(6)之Group By/Having
    LINQ to SQL语句(5)之Order By
    LINQ to SQL语句(4)之Join
    LINQ to SQL语句(3)之Count/Sum/Min/Max/Avg
    LINQ to SQL语句(2)之Select/Distinct
    java 开发工具记录
    H5播放器内置播放视频(兼容绝大多数安卓和ios)
  • 原文地址:https://www.cnblogs.com/ac-evil/p/10338316.html
Copyright © 2020-2023  润新知