zoukankan      html  css  js  c++  java
  • 1014下午考试

    1014下午考试

    T1

    ​ 题目大意:

    ​ 有一个(n*m)的矩阵,矩阵的每个位置上可以放置一个数。对于第i行,第i行的差异定义为该行的最大数和最小数的差。一个矩阵的差异,定义为矩阵中每一行差异的最大值。现在给定k个数v[1..k],问:从这k个数中选(n*m)个数放入矩阵,能够得到的矩阵的差异最小值是多少。

    ​ n * m <= k <= 100000, n, m <= 1000,0<= v[i] <= 10^9

    ​ 二分。

    ​ 就是二分的板子题,先对每个数排序,然后二分矩阵差异最小值,贪心的判断是否可以选出n个长度为m的连续子段。

    #include <bits/stdc++.h>
    
    #define mid ((l + r) >> 1)
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 1e5 + 5, inf = 1e9;
    int k, n, m, ans;
    int a[N], vis[N];
    
    int check(int s) {
        int res = 0;
        for(int i = 1;i <= k; i++) {
            int tag = 0, tmp = 1;
            for(int j = i + 1;j <= k; j++) {
                if(a[j] - a[i] > s) { tag = 1; break; }
                else { tmp ++; }
                if(tmp == m) { i = j; break; }
            }
            if(tag == 0 && tmp == m) res ++;
        }
        return res >= n;
    }
    
    int main() {
    
        k = read(); n = read(); m = read();
        for(int i = 1;i <= k; i++) a[i] = read();
        sort(a + 1, a + k + 1);
        int l = 0, r = inf;
        while(l <= r) {
            if(check(mid)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        printf("%d", ans);
    
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T2

    ​ 题目大意:

    ​ 给定一个长度为n的序列v[1..n],现在要将这个序列分成k段(每段都不能为空),定义每一段的权值为该段上的所有数的或和。定义整个序列的权值为每段权值的和。问:这个序列的最大权值为多少。

    ​ k <= n <= 2000,1<= v[i] <= 5 *10^5

    ​ 线性DP。

    ​ 考场上想出来60分的做法:

    (f[i][j])表示前(i)个数分成(j)段的最大权值,那么转移方程就是:(f[i][j] = max(f[i][j], f[l][j - 1] + or[l + 1][i]))

    ​ 复杂度(O(n ^ 3))

    ​ 考虑优化一下,或和从0变到10^6大概需要20次,如果说没变,我们只需要取没变的这一段(f)值最大的一个就好了。因为(or)随着长度变小单调不升,(f)随着长度变大单调不降,所以这一段最后一个点就是断点。所以我们可以与处理出断点在哪,第三层循环直接枚举这些断点就好了。

    ​ 复杂度(O(n^2logn))

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 2005;
    int n, k;
    long long a[N], aor[N][N], f[N][N];
    vector <long long> v[N];
    
    int main() {
    
        n = read(); k = read();
        for(int i = 1;i <= n; i++) a[i] = read();
        for(int l = 1;l <= n; l++) 
            for(int r = l;r <= n; r++) 
                aor[l][r] = aor[l][r - 1] | a[r];
        for(int r = 1;r <= n; r++) 
            for(int l = 1;l <= r; l++) 
                if(aor[l][r] != aor[l + 1][r]) v[r].push_back(l);
        for(int i = 1;i <= n; i++) f[i][1] = aor[i][1];
        for(int i = 2;i <= n; i++) 
            for(int j = 1;j <= min(i, k); j++) 
                for(int l = 0;l < (int)v[i].size(); l++) 
                    f[i][j] = max(f[i][j], f[v[i][l] - 1][j - 1] + aor[v[i][l]][i]);
        printf("%lld", f[n][k]);
    
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T3

    ​ 题目大意:

    ​ 有一棵k+1层的满二叉树,那么该树有2^k 个叶子节点。给定n个机器人(n=2^k),编号从1—n,编号为i的机器人的权值为v[i]。我们现在要将这n个机器人分别放置在这n个叶子节点上,每个叶子节点放且只能放一个机器人。叶子节点的权值为该叶子节点上的机器人的权值。非叶子节点的权值定义为该树中编号最大的机器人的权值。每个非叶子节点除了权值以外,还有一个魔法值,该点的魔法值为其左右儿子节点的权值的乘积。整棵树的魔法值定义为非叶子节点的魔法值的和。问:将这n个机器人随机地放在这n个叶子节点上,树的魔法值的期望为多少。

    ​ 假设答案为一个不可约分数P/Q,则输出在模1e9+7意义下的P* (Q^-1)模1e9+7的值。k <= 18。

    ​ 组合数 + 推式子

    ​ 我们假设当先要合并的点高度为d,左子树权值为x,右子树权值为y,x > y;

    ​ 那么贡献是这个:(C(y - 1, 2^d - 1) * C(x - 2 ^ d - 1, 2^d - 1) * v[x] * v[y] * (n - 2^{d + 1})! * 2 * 2^{k - d} * 2^d! * 2^d!)

    ​ 解释一下:

    ​ 高度为d的二叉树节点的子树中有(2 ^ d)个节点,要从比(y)小的数里面选出(2^d - 1)个数,顺序不同方案也不同,所以是:(C(y - 1, 2 ^ d - 1) * 2 ^ d!)

    ​ 除去(y)的子树中的节点后,要从比(x)小的数里面选出(2^d - 1)个数,顺序不同方案也不同,所以是:(C(x - 2^d - 1, 2 ^ d - 1) * 2 ^ d!)

    ​ 除去(x, y)的所有节点后,剩下的数随便排,所以是:((n - 2 ^ d - 2^d)! = (n - 2^{d + 1})!)

    (x, y)的顺序可以交换,所以再乘2。

    ​ 当前高度为d,那么层数就是(k - d)层,二叉树这一层会有(2 ^ {(k - d)})个节点,都可以在这上面合并,所以再乘(2 ^ {k - d})

    ​ 最后再乘上(x, y)的权值(v[x], v[y])

    ​ 复杂度(O(kn ^ 2))

    ​ 可以后缀和优化一下,首先原式子可以写成:

    (displaystyle sum_{d} sum_{x} sum_{y} * A(y - 1, 2^d - 1) * A(x - 2^d - 2, 2^d - 1) * (n - 2^{d + 1})! * 2^{k - d + 1})

    ​ 还可以写成:

    (displaystyle sum_{d} (n - 2^{d + 1})! * 2^{k - d + 1} sum_{y} A(y - 1, 2^d - 1) sum_{x} A(x - 2^d - 2, 2^d - 1))

    ​ 预处理出后面那两坨sigema的后缀和就好了,复杂度(O(kn))

    #include <bits/stdc++.h>
    
    #define ls(o) (o << 1)
    #define rs(o) (o << 1 | 1)
    #define mid ((l + r) >> 1)
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 3e5 + 5, mod = 1e9 + 7;
    int k, n;
    int ans, a[N];
    long long fac[N], inv[N], bit[N], sum[N];
    
    void make_pre() {
        fac[0] = fac[1] = inv[0] = inv[1] = bit[0] = 1;
        for(int i = 2;i < N; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
        for(int i = 2;i < N; i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
        for(int i = 2;i < N; i++) inv[i] = 1ll * inv[i - 1] * inv[i] % mod;
        for(int i = 1;i < N; i++) bit[i] = bit[i - 1] * 2;
    }
    
    int C(int x, int y) {
        if(x < y) return 0;
        return 1ll * fac[x] * inv[y] % mod * inv[x - y] % mod;
    }
    
    int main() {
    
        make_pre();
        k = read(); n = bit[k];
        for(int i = 1;i <= n; i++) a[i] = read();
        for(int d = 1;d <= k; d++) {
            for(int x = n;x >= bit[d]; x--) 
                sum[x] = (sum[x + 1] + 1ll * C(x - bit[d - 1] - 1, bit[d - 1] - 1) * a[x] % mod) % mod;
            long long tmp = 0;
            for(int y = n;y >= bit[d - 1]; y--) 
                tmp = (tmp + 1ll * C(y - 1, bit[d - 1] - 1) * a[y] % mod * sum[max(bit[d], (long long) y + 1)] % mod) % mod;
            ans = (ans + 1ll * tmp * fac[bit[d - 1]] % mod * fac[bit[d - 1]] % mod * fac[n - bit[d]] % mod * 2 % mod * bit[k - d] % mod) % mod;
        }
        printf("%d", 1ll * ans * inv[n] % mod);
    
        fclose(stdin); fclose(stdout);
        return 0;
    }
    
  • 相关阅读:
    C++ CheckListBox
    TreeView查获节点并选中节点
    创建文件自动重命名
    bat
    Edit显示行号
    FindStringExact
    Extended ComboBox添加图标
    C++ Combobox输入时自动完成
    C++ ComboBox基础
    C++ Code_combobox
  • 原文地址:https://www.cnblogs.com/czhui666/p/13818616.html
Copyright © 2011-2022 走看看