zoukankan      html  css  js  c++  java
  • luogu2467 [SDOI2010]地精部落

    题目大意

      求在$[1,n]$的排列中是波动序列的数量。

    题解

    性质

      当我们对波动序列$a$进行以下操作时,得到的新序列仍然是个波动序列:

    1. 若$a_i = a_j+1且|j-i|>1$,将$a_i,a_j$交换。
    2. 将波动序列上下翻转(也就是$forall a_i, a_i ightarrow n-a_i +1$)。
    3. 将波动序列左右翻转(也就是$forall a_i, a_i ightarrow a_{n-i+1}$)。

      另外有性质1:对于任意一个长度为$n$,数值两两不同,且数值取值范围固定但不限于$[1,n]$的波动序列$a$,它的种类数与长度为$n$,数值取值等于$[1,n]$的序列的种类数是相同的。

    状态的设计

      由操作3我们可以想到:若我们规定每个波动序列的第一个数字都是山峰,那么最后我们的结果就是这些波动序列的数量*2,所以我们要用$j$表示第一个数字为$j$且它为山峰;因为一个波动序列的每一个子序列都满足性质1,所以我们要用$i$来表示波动序列长度为$i$,且数值取值范围等于$[1,i]$的结果。$f$表示这样的种类。状态$f(i,j)$就出来了。

    状态的转移

      由序列中元素$a_1$与$a_2$的关系分两种情况。

    • 若$a_2<a_1-1$,对$a_1$该值操作1,得到$f(i,j-1)$。
    • 若$a_2=a_1-1$,将子序列$[2,n]$内的元素由性质1可以对应到一个长度为$i-1$取值范围等于$[1,i-1]$的波动序列,将该序列进行操作2,得到$f(i-1,i-j+1)$

      所以最后的递推式为$f(i,j)=f(i,j-1)+f(i-1,i-j+1)$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define F(i, j) Dp[(i) & 1][j]
    const int MAX_N = 5000;
    long long Dp[2][MAX_N], P;
    int N;
    
    long long DP()
    {
    	F(2, 2) = 1;
    	for (int i = 3; i <= N; i++)
    	{
    		memset(Dp[i & 1], 0, sizeof(Dp[i & 1]));
    		for (int j = 2; j <= i; j++)
    			F(i, j) = (F(i, j - 1) + F(i - 1, i - j + 1)) % P;
    	}
    	long long ans = 0;
    	for (int i = 2; i <= N; i++)
    		ans = (ans + F(N, i)) % P;
    	return (ans * 2) % P;
    }
    
    int main()
    {
    #ifdef _DEBUG
    	freopen("c:\noi\source\input.txt", "r", stdin);
    #endif
    	scanf("%d%lld", &N, &P);
    	printf("%lld
    ", DP());
    	return 0;
    }
    

      

  • 相关阅读:
    开源TinyXML 最简单的新手教程
    2014第14周一开发问题记
    2014第13周日
    140329
    2014第13周五
    2014第13周四Webservice概念问题记
    2014第13周三
    2014第13周二
    2014第13周一
    2014第12周日
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9509525.html
Copyright © 2011-2022 走看看