zoukankan      html  css  js  c++  java
  • 【题解】SDOI2010地精部落

    强!强!强!强!劲啊劲啊劲啊!!!洛谷P2467

    非常重要的,就在于发现以下的两条性质:

    1.当i与i+1不相邻时,方案数是一样的:交换这两个数,<i+1的必然<i,>i+1的必然>i,又因为i+1与i不相邻,所以>i的>i+1,<i+1的也<i。

    2.将每个数变成(n+1-i)时,仍然是满足性质的波动序列,且山峰与山谷反置。这个怎么理解呢?可以当做补全柱状图,画一画就是一个显而易见的结论。

    那么我们定义状态dp[i][j]为将前i个数排列组合,且j为第一个山峰的合法方案数。

    由第一条性质可得:当j与j-1不相邻的时候,dp[i][j]=dp[i][j-1];

    那么当j与j-1相邻的时候呢?可以注意到,j>j-1,j为山峰,j-1为山谷。我们所求的实际上就是包含了1-j-1,j+1-i这些数且开头为j-1的山谷的方案数。

    这个东西要怎样通过之前的状态表示出来呢?可以发现有两个障碍:1.区间不连续 2.开头为山谷。

    针对1,我们发现方案数与数字的大小并无关系,只与数字的相对大小有关。而j+1-i中的所有数都>1-j-1的数,将前一部分中的每个数减去1,就可以得到1-i-1的连续区间,且因为没有改变相对位置的关系,所以方案数是相同的。

    针对2,我们运用第2条性质,将山峰与山谷反置。

    综上所述,我们所求的即是dp[i-1][(i-1)+1-(j-1)] --> dp[i-1][i-j+1];

    所以得到完整的dp方程:dp[i][j]=dp[i][j-1]+dp[i-1][i-j+1];为了节约空间,我们可以使用滚动数组来计算。

    答案为sigma(dp[n][i])(i=2~n)*2.为什么要乘以二?以x为山峰的所有方案反置即得到以(n+1-x)为山谷的方案数,所以要计算两次。

    非常棒的一道题目呀

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 4300
    #define ll long long 
    ll n, p, dp[2][maxn], ans;
    int main()
    {
        scanf("%lld%lld", &n, &p);
        int pre = 0, now = 1;
        dp[0][2] = 1;
        for(int i = 3; i <= n; pre ^= 1, now ^= 1, i ++)
            for(int j = 2; j <= i; j ++)
                dp[now][j] = (dp[now][j - 1] + dp[pre][i - j + 1]) % p;    
        now ^= 1;
        for(int i = 2; i <= n; i ++) ans = (ans + dp[now][i]) % p;
        printf("%lld", (ans << 1) % p);
        return 0; 
    } 
  • 相关阅读:
    myeclipse-9.0安装svn客户端插件
    jquery中lhgdialog插件(一)
    Jquery之ShowLoading遮罩组件
    git 命令
    org.apache.ibatis.builder.IncompleteElementException: Could not find parameter map
    在线压缩图片网址
    如何在知网下载外文文献
    Linux 常见命令
    在VIM 里面编辑和保存
    vi a.sh ABCD
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/8424541.html
Copyright © 2011-2022 走看看