zoukankan      html  css  js  c++  java
  • 1925: [Sdoi2010]地精部落 dp, 抖动子序列

    看到这道题第一反应就把该题与白书的一道例题联系起来了。(虽然后来证明两者并没有联系。)因此我一开始的思路就是从n到1一个个加进去。虽然的确搞不太出来。

    然后开始膜题解了。………………………………好可耻啊!

    首先可以证明。一个开头下降的抖动子序列 1~n 可以通过 n - xi + 1 (xi 为 第i位的值 )的形式变为一个上升的。

    然后就利用这个性质搞了

    设 f[i][j] 为长度为 i , 分别以 1~j 开头且开头上升(如果你要问我为什么是上升的你可以自己推一下,反正我推不出来。)的方案数(输出时答案*2就好)。

    递推式 f[i][j] = f[i][j-1] + f[i-1][i-j]

    f[i][j-1] 就是以 1~j-1 开头的。 那么很明显 f[i-1][i-j] 就是以 j 开头的了。   怎么推导呢? 

    如果以 j 开头,且序列为 1~i 又因为开头要上升。所以第二位的取值范围为 j+1~i 又因为为抖动子序列。 那么第二位开始要下降。 可我们求的是下降的怎么办呢(或者说已有的表现形式是上升的)?

    我们可以用一开始的性质把下降的转化成上升的。如果一开始的取值范围为 j+1 ~ i 那么通过 n - xi + 1  转化后, 开头的取值范围就变为了 1 ~ i-j。 (如果之前的表示是上升的会在这里出问题。) 那么很明显就是 f[i-1][i-j] 了,至于为什么是 i-1, 因为序列的长度只有 i-1 个了。就这样。

     1 #include<cstdio>
     2 #include<iostream>
     3 #define rep(i,j,k) for(register int i = j; i <= k; i++)
     4 using namespace std;
     5 
     6 inline int read() {
     7     int s = 0, t = 1; char c = getchar();
     8     while( !isdigit(c) ) { if( c == '-' ) t = -1; c = getchar(); }
     9     while( isdigit(c) ) s = s * 10 + c  - 48, c = getchar();
    10     return s * t;
    11 }
    12 
    13 int f[2][4201];
    14 
    15 int main() {
    16     int n = read(); register int p = read(), now = 1, pre = 0;
    17     f[now][1] = 1;
    18     rep(i,2,n) {
    19         swap(now,pre);
    20         rep(j,1,i) {
    21             f[now][j] = f[now][j-1] + f[pre][i-j];
    22             if( f[now][j] >= p ) f[now][j] -= p;  
    23         }
    24     }
    25     cout<<f[now][n] * 2 % p<<endl;
    26     return 0;
    27 }
  • 相关阅读:
    图像轮廓缺陷修补
    VS2005,VS2008,VS2010工程文件和解决方案的区别
    MFC中调用WPF教程
    Code::Blocks与wxWidgets安装配置——基于C++的免费IDE开发平台
    kalman 滤波 演示与opencv代码
    Predator:比微软Kinect更强的视频追踪算法来自捷克博士论文
    二值图像轮廓提取
    有用网址
    如何在Rich Edit Control中管理超链接
    坐标轴的平移与旋转
  • 原文地址:https://www.cnblogs.com/83131yyl/p/5466918.html
Copyright © 2011-2022 走看看