zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest #026 D

    Time Limit: 2 sec / Memory Limit: 1024 MB

    Score : 11001100 points

    Problem Statement

    Let us consider a grid of squares with 109109 rows and NN columns. Let (i,j)(i,j) be the square at the ii-th column (1iN)(1≤i≤N) from the left and jj-th row (1j109)(1≤j≤109) from the bottom.

    Snuke has cut out some part of the grid so that, for each i=1,2,...,Ni=1,2,...,N, the bottom-most hihi squares are remaining in the ii-th column from the left. Now, he will paint the remaining squares in red and blue. Find the number of the ways to paint the squares so that the following condition is satisfied:

    • Every remaining square is painted either red or blue.
    • For all 1iN11≤i≤N−1 and 1jmin(hi,hi+1)11≤j≤min(hi,hi+1)−1, there are exactly two squares painted red and two squares painted blue among the following four squares: (i,j),(i,j+1),(i+1,j)(i,j),(i,j+1),(i+1,j) and (i+1,j+1)(i+1,j+1).

    Since the number of ways can be extremely large, print the count modulo 109+7109+7.

    Constraints

    • 1N1001≤N≤100
    • 1hi1091≤hi≤109

    Input

    Input is given from Standard Input in the following format:

    NN
    h1h1 h2h2 ...... hNhN
    

    Output

    Print the number of the ways to paint the squares, modulo 109+7109+7.


    Sample Input 1 Copy

    Copy
    9
    2 3 5 4 1 2 4 2 1
    

    Sample Output 1 Copy

    Copy
    12800
    

    One of the ways to paint the squares is shown below:

      #
      ##  #
     ###  #
    #### ###
    #########
    

    Sample Input 2 Copy

    Copy
    2
    2 2
    

    Sample Output 2 Copy

    Copy
    6
    

    There are six ways to paint the squares, as follows:

    ## ## ## ## ## ##
    ## ## ## ## ## ##
    

    Sample Input 3 Copy

    Copy
    5
    2 1 2 1 2
    

    Sample Output 3 Copy

    Copy
    256
    

    Every way to paint the squares satisfies the condition.


    Sample Input 4 Copy

    Copy
    9
    27 18 28 18 28 45 90 45 23
    

    Sample Output 4 Copy

    Copy
    844733013
    

    Remember to print the number of ways modulo 109+7109+7.

    按照题目的要求,四个字符组成的正方形区域内两种颜色要各占一半。
    先看一个例子

    2

    3 3

    图案为

    ##

    ##

    ##

    方案有这么几种(两种字符代表两种颜色)

    |*#|#*|#*|*#|*#|#*|*#|#*|** |##

    |#*|*#|*#|*#|*#|#*|#*|#*|##|**

    |*#|#*|*#|#*|*#|#*|#*|*#|** |##

    一共十种方案

    可以总结的规律是如果一列中的颜色没有连续相同的,比如第1,2,9,10种方案,那么在第一列确定的情况下,第二列还可以有两种方案,但是一旦有连续相同的了,比如第k和k+1颜色相同,那么在第二列的k和k+1(如果存在的话)颜色就是已经确定的了,这样其他位置的颜色也是确定的,所以只有一种方案。如此就可以分开来存储有连续相同的方案数,和交替颜色(无连续相同)的方案数。

    用dp[i][0]存无连续相同的方案数。而dp[i][j]分两种情况。

    由于每一列高度不尽相同,还有考虑各种情况。

    对于每一列,求无连续相同颜色的方案,只是局限于其与前一列接触的部分,不接触的部分颜色可以随意设置,至于下一列是否会接触这一部分再又下一列去选择。

    dp[i][j]呢,对于与前一列有接触的部分,dp[i][j]存 有连续相同的方案数,假如这一列比前一列高出d个,那么dp[i][j] = dp[i - 1][j] * 2 ^ d,对于高出的部分是很灵活的,不会受前一列的限制,可以随意变化,至于高出来这一块的dp[i][j]存无连续相同的方案数(有连续相同的方案 在底部接触部分已经记录过)。

    由于高度可能很大,所以这里采用离散化,把所有的高度存到一个数组里,这样每个列对应一个高度的下标。由于一共有不多于100列,所有最多有100个不相同的高度,直接按照高度差来计算相关量即可。

    先看c++代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int MOD = (int)1e9 + 7;
    
    int n,m,h[101],hnum[101],dh[101];///h记录每一列高度 dh记录离散化后的各不相同的高度 hnum记录h对应dh中的位置,即第几高的高度
    long long dp[101][101];///记录第i列第n块高度往上的方案数
    
    int pow_(long long x,int y) {///快速幂求 x的y次方
        long long ans = 1;
        if(y > 0) {
            while(y) {
                if(y % 2)ans = ans * x % MOD;
                x = x * x % MOD;
                y /= 2;
            }
        }
        return ans;
    }
    
    int main() {
        scanf("%d",&n);
        for(int i = 1;i <= n;i ++) {
            scanf("%d",&h[i]);
            dh[++ m] = h[i];///先把高度存到dh数组 下标从1开始,方便后面dp
        }
        sort(dh + 1,dh + m + 1);///dh数组排序
        m = unique(dh + 1,dh + m + 1) - dh - 1;///dh数组离散化去掉重复的高度 m是不相同的高度数
        for(int i = 1;i <= n;i ++) {
            hnum[i] = lower_bound(dh + 1,dh + m + 1,h[i]) - dh;///每一列高度在dh数组中对应的位置
        }
        dp[0][0]=1;///初始化 当第0列有1个无连续相同的方案
        for(int i = 1;i <= n;i ++) {///对每一列每个高度段进行更新 更新过程为从下往上
            (dp[i][0] += dp[i - 1][0] * 2 % MOD) %= MOD;///无连续相同方案  加上前i - 1列无连续相同方案数*2 因为可以是与前一列对应位置同色或者异色一共两种方案
            for(int j = hnum[i] + 1;j <= hnum[i - 1];j ++)///如果前一列比这一列高,高出的部分也存着接触部分无连续相同的方案 但是可能会重复加上上一步的方案数所以下面更新高出的部分时会避免
                (dp[i][0] += dp[i - 1][j] * 2 % MOD) %= MOD;
            int d = pow_(2,h[i] - h[i - 1]);///比前一列高出部分的涂色方案数,如果比前一列低 d就等于1
            for(int j = 1;j <= min(hnum[i - 1],hnum[i]);j ++) {
                dp[i][j] = dp[i - 1][j] * d % MOD;///有连续相同颜色的方案数 已经分析过接触部分的颜色一定是定下的 所以这一列接触部分有连续相同部分的方案数由高出部分的变化决定 即 乘上d
            }
            for(int j = hnum[i - 1] + 1;j <= hnum[i];j ++) {///更新比前一列高出的部分 j从上一列的高度加1的下标开始
                if(j > 1)(dp[i][j] = dp[i - 1][0] * (pow_(2,dh[j] - dh[j - 1]) - 1) % MOD * 2 % MOD * pow_(2,h[i] - dh[j]) % MOD) %= MOD;
                ///一般情况 前i - 1列无连续方案 * (第j块高度变化方案 -1表示去掉无连续相同方案) * 2 * 剩下几块高度的变化方案数
                else (dp[1][1] = dp[0][0] * (pow_(2,dh[1]) - 2) % MOD * pow_(2,h[i] - dh[1]) % MOD) %= MOD;
                ///第一列的第一块高度 记录随意变化方案数 -2表示除去dp[i][0]已经记录过的无连续相同方案 勿重复记录
            }
        }
        long long ans = 0;
        for(int i = 0;i <= hnum[n];i++)
            (ans += dp[n][i]) %= MOD;
        printf("%lld",ans);
        return 0;
    }
    View Code

    # ## # ### # #### ### #########
    对于样例一来说,最初dp[0][0] = 1;dh[] = {0,1,2,3,4,5},hnum[] = {0,2,3,5,4,1,2,4,2,1},然后一列一列来
    第一列:dp[1][0] = dp[0][0] * 2 = 2,2个无连续相同方案,然后前一列高度为0,所以第一列比前一列高出两个高度块,dp[1][1] = 0,dp[1][2] = 2;
    第二列:dp[2][0] = dp[1][0] * 2 = 4,dp[2][1] = dp[1][1] * 2 = 0,dp[2][2] = dp[1][2] * 2 = 4;比前一列高出3 - 2 = 1个高度块,dp[2][3] = 4;
    第三列:dp[3][0] = dp[2][0] * 2 = 8,dp[3][1] = dp[2][1] * 4 = 0,dp[3][2] = dp[2][2] * 4 = 16,dp[3][3] = dp[2][3] * 4 = 16,比前一列高出两个高度块,dp[3][4] = dp[2][0] * 2 * 2 = 16,dp[3][5] = 8;
    第四列:dp[4][0] = dp[3][0] * 2 + dp[3][5] * 2 = 32,dp[4][1] = 0,dp[4][2] = dp[3][2] = 16,dp[4][3] = 16,dp[4][4] = 16;
    第五列:dp[5][0] = dp[4][0] * 2 + dp[4][2] * 2 + dp[4][3] * 2 + dp[4][4] * 2 = 160,dp[5][1] = 0;
    第六列:dp[6][0] = dp[5][0] * 2 = 320,dp[6][1] = 0,dp[6][2] = dp[5][0] * 2 = 320;
    第七列:dp[7][0] = dp[6][0] * 2 = 640,dp[7][1] = 0,dp[7][2] = dp[6][2] * 4 = 1280,dp[7][3] = dp[6][0] * 2 * 2 = 1280,dp[7][4] = dp[6][0] * 2 = 640;
    第八列:dp[8][0] = dp[7][0] * 2 + dp[7][3] * 2 + dp[7][4] * 2 = 5120,dp[8][1] = 0,dp[8][2] = dp[7][2] = 1280;
    第九列:dp[9][0] = dp[8][0] * 2 + dp[8][2] * 2 = 12800,dp[9][1] = 0;
    答案:12800

  • 相关阅读:
    oracle的nvl函数的用法
    简单实用的MD5加密算法
    oracle触发器使用笔记
    Html学习
    连接字符串
    oracle触发器使用笔记2
    oracle中如何给有空值的数据排序
    Zend Frame 添加Smarty模板引擎
    HDU 2464 A Pair of Graph
    POJ 1466 Girls and Boys
  • 原文地址:https://www.cnblogs.com/8023spz/p/9406634.html
Copyright © 2011-2022 走看看