• Luogu 2467 [SDOI2010]地精部落


    挺有意思的题。

    优质题解: https://www.luogu.org/blog/user55639/solution-p2467

    题意为求长度为n,取值为$[1, n]$的波动序列的个数。

    首先需要三个性质:

    性质1:在一个波动序列中,如果数字$i$与数字$i - 1$不相邻,那么把$i$与$i - 1$交换之后也会构成一个波动序列

    性质2:如果已经构造好了一个波动序列,那么把这个序列中的每一个数$a_{i}$全部变成$(n + 1 - a_{i})$也是一个波动序列,且山谷和山峰的位置相反

    性质3:一个波动序列倒过来也是一个合法的波动序列

    首先根据性质3,我们只要算出以山峰开头的波动序列的个数,然后乘以二就是最终的答案了。

    我们设$f_{i, j}$表示选用区间$[1, i]$中的数,且数字$j$开头为山峰的方案数。

    考虑转移:    $f_{i, j} = f_{i, j - 1} + f_{i - 1, i - j + 1}$ $(2leq jleq i )$

    解释一下这个转移方程,由于性质1,当数字$j$与数字$j - 1$不相邻的时候,我们直接交换数字$j$和数字$j - 1$可以构造出合法的波动序列

    考虑数字$j$与数字$j - 1$相邻的情况,这时候我们构造的序列相当于这样

    $(j), (j - 1), ..., ..., ...$

    因为$j$一定是一个山峰的位置,那么$j - 1$一定是一个山谷的位置,那么相当于我们要加上数字取值在$[1, j - 1] cup [j + 1, i]$且以数字$j - 1$开头的山谷的数量

    我们的状态设计并不能拆分区间,但是我们可以考虑把$[j + 1, i]$区间的数字全部都向左平移一位,这样并不会改变构造的波动序列的合法性

    由于性质2,我们所求的这一段答案就是$f_{i - 1, (i - 1) + 1 - (j - 1) = i - j + 1}$。

    考虑到1开头作山峰的时候没有合法的波动序列,所以初态从2开始。

    滚掉第一维即可。

    时间复杂度$O(n ^ {2})$

    思维量和码量的差距还是很大的。

    Code:

    #include <cstdio>
    using namespace std;
    
    const int N = 4205;
    
    int n, P, f[2][N];
    
    int main() {
        scanf("%d%d", &n, &P);
        f[0][2] = 1;
        for(int i = 3; i <= n; i++)
            for(int j = 2; j <= i; j++)
                f[i & 1][j] = (f[i & 1][j - 1] + f[(i - 1) & 1][i - j + 1]) % P;
        
        int ans = 0;
        for(int i = 2; i <= n; i++)
            ans = (ans + f[n & 1][i]) % P;
            
        printf("%d
    ", ans * 2 % P);
        return 0;
    }
    View Code

    我真的不会数数呜呜呜……

  • 相关阅读:
    Qt编写控件属性设计器12-用户属性
    C#中通过三边长判断三角形类型(三角形测试用例)
    C#中通过Selenium定位<a>标签的问题
    SharePoint自动化系列——Manage "Site Subscriptions" using PowerShell
    SharePoint API测试系列——Records.BypassLocks测试
    SharePoint API测试系列——对Recorded Item做OM操作(委托的妙用)
    放松时刻——C#分割字符串
    链表——PowerShell版
    栈——PowerShell版
    队列——PowerShell版
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9486266.html
Copyright © 2020-2023  润新知