zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 83 (Rated for Div. 2)

    题目链接:https://codeforces.com/contest/1312

    A - Two Regular Polygons

    送分题。

    B - Bogosort

    题意:给一个数组a进行重排,使得新的数组a中,记b[i]=a[i]-i,则b[i]是唯一的。

    题解:从大到小排序,这样就有b[i+1]=a[i+1]-(i+1)<a[i+1]-i<=a[i]-i<b[i]。

    C - Adding Powers

    题意:给一个n个数的数组b,另一个n个数的数组a初始全是0,第i步(i从0开始计数)可以选择数组a中的一个数加上k^i,或者跳过这一步,问是否能把数组a变成数组b。

    题解:对每个数进行k进制分解,看看是否k进制下每一位的和都是<=1的。

    D - Count the Arrays

    可以初步观察出一些事实:
    1、最大的数是只有一个的,否则不会满足左右都是严格升降序。
    2、最大的数的取值范围为 ([n-1,m])
    3、最大的数不能放在两端。
    4、相等的数分布在最大的数两侧。

    算法1,给位置选数

    先枚举最大的数的位置,设其前面有 (x) 个位置,后面有 (y) 个位置,则 (xge 0,y ge 0,x+y=n-1)
    再枚举最大的数的取值,观察可以发现最大的数的取值是不受位置影响的,设取值为 (p in [n-1,m])
    再从 ([1,p-1]) 中选 (x) 个数给前面 (x) 个位置,选法为 (C_{p-1}^{x})
    再从前面 (x) 个数中选 (1) 个数作为相等的数字放在后面的某个位置, 选法为 (C_{x}^{1})
    再从 ([1,p-1]) 个数中选 (y-1) 个数给后面剩下的 (y-1) 个位置,且这些数不能与前面 (x) 个数相等,选法为 (C_{p-1-x}^{y-1})

    这里会发现复杂度爆炸,但是这个形式看起来有规律。

    [egin{align} &C_{p-1}^{x}*C_{x}^{1}*C_{p-1-x}^{y-1} onumber \ &=frac{(p-1)!}{(p-1-x)!x!}*x*frac{(p-1-x)!}{(p-1-x-(y-1))!(y-1)!} onumber \ &=frac{(p-1)!}{(p-n+1)!(x-1)!(y-1)!} onumber \ &=frac{1}{(x-1)!(y-1)!}*frac{(p-1)!}{(p-n+1)!} onumber \ end{align} ]

    后面的部分与 (x,y) 无关,前面的部分与 (p) 无关,两部分可以分开统计了。

    算法2,给数选位置

    先从 ([1,m]) 中选出 (n-1) 个数,然后可以把他们离散化到 ([1,n-1])
    然后从 ([1,n-2]) 中选出那个相等的数(因为 (n-1) 是最大的数,不能相等),放在最大的数两边。
    剩下的数既可以放在左边,也可以放在右边,有 (2^{n-3}) 种选法。

    反思:怪不得这道题这么多人过,算法2是真的简单,所以以后组合数学可以从“给位置选数”以及“给数选位置”两种角度考虑。

    关键是得知了: (C_{n}^{x}*C_{n-x}^{y} e C_{n}^{x+y}) 而是 (C_{n}^{x}*C_{n-x}^{y} e C_{n}^{x+y}*C_{x+y}^{x}) ,也就是说,等式左侧的规定了哪些数是去 (x) 中的,所以在等式右侧也要规定。

    *E - Array Shrinking

    第一次见区间dp可以这样做的。以前的区间dp貌似都是枚举“最后一次合并区间时发生的状态转移”,但这里的两个区间合并的时候要保证“左右的剩下元素的左右边界相同”。这里就发现自己是没真正明白区间dp的。枚举的是“最后一次合并区间时发生的状态转移”,所以必须是“最后一次合并区间”,换言之,假如枚举的区间是[5 4 3 3][3 3 4 5],回答的就应该是[7],为什么不用考虑下面的[3 3]或者3][3合并呢?因为这些合并应该在长度更小(长度为2)的时候被处理完,并不是“最后一次合并区间”。换言之,在枚举断点,合并左右两端的时候,只需要知道两点:

    1、左区间和右区间是否分别能合并成一个数,使得枚举断点的位置确实成为“最后一次合并”。

    2、左区间和右区间能合并成的一个数是否相同,使得枚举断点的位置确实成为“最后一次合并”。

    int a[505];
    int b[505][505];
    int dp[505][505];
    
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) {
                dp[i][j] = INF;
                b[i][j] = 0;
            }
        }
        for(int i = 1; i <= n; ++i) {
            dp[i][i] = 1;
            b[i][i] = a[i];
        }
        for(int len = 2; len <= n; ++len) {
            for(int l = 1; l <= n; ++l) {
                int r = l + len - 1;
                if(r > n)
                    break;
                for(int m = l; m < r; ++m) {
                    dp[l][r] = min(dp[l][r], dp[l][m] + dp[m + 1][r]);
                    if(b[l][m] != 0 && b[l][m] == b[m + 1][r]) {
                        dp[l][r] = 1;
                        b[l][r] = b[l][m] + 1;
                        break;
                    }
                }
            }
        }
        printf("%d
    ", dp[1][n]);
    }
    

    总结:其实就是短的合并都已经被下层枚举了,比如[5 4 3 3 3 3 4 5],假如要问[4 3 3]是否可以合并成一个数,就向下询问[4][3 3]是否可以分别合并成一个数,且这两个数相等(并不需要事先知道要合并成几,因为每个区间完全合并得到的数是唯一的,dfs得到左边之后询问右边即可),再问[4 3][3]行不行,以此类推。

  • 相关阅读:
    纹理mag filter不能取GL_XXX_MIPMAP_XXXX
    (转)No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=arm64, VA 解决办法
    轻松制作儿童趣味算术软件
    批处理设置IP地址
    安卓手机文件管理器简单横向评比
    Linux基础和网络管理上机试题
    值得收藏的批处理程序
    王垠:完全用Linux工作
    XINU安装程序.exe一键配置好XINU实验环境
    很全面的WinRAR实用技巧系列
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12452921.html
Copyright © 2011-2022 走看看