zoukankan      html  css  js  c++  java
  • 「csp校内训练 2019-11-06」解题报告

    「csp校内训练 2019-11-06」解题报告

    T1、华中

    题目链接

    (Description)

    严先生是华中科技大学的大一新生。
    体育选课了,严先生作为幸运 Ex 的联创人才,选上了「户外运动」这门课。

    严先生第一次课的内容是“定向越野”。
    具体来说是这样的:校园内共有 (n) 个检查点,他们之间有 (n - 1) 条路径相连。
    换句话说,检查点之间构成了一棵树。

    为了让事情更有趣一点,严先生将这些检查点划分为两种,权值分别为 (0)(1)
    但是学校有打卡规定,在一次越野之后,经过的所有检查点的权值异或和必须为 (0)

    严先生的越野方案是这样的:从某一个检查点开始,不经过重复的检查点,随机的选择一个与当前检查点有路径相连的检查点,直到走到无路可走,这算完成一次定向越野。
    但是严先生是联创团队 lab 组的优秀人才,严先生可以随意钦点每一个检查点的权值。
    也就是说,严先生会让每一个点轮流做一次根节点,你需要让根节点到每一个叶子节点的路径权值异或和都为 (0) ,这就是一个合法的方案。
    现在严先生想知道,总共有多少种合法的方案。输出结果对 $ 10^9 + 7 $ 取模。

    $ 1 leq n leq 10^6 $。

    (Solution)

    先考虑 (dp)(f_u) 表示 (u) 为根的子树中的叶子节点到 (u) 的异或和相等的方案数;
    显然有 (f_u = 2 prod f_v),对于叶子有 (f_u = 1)
    直接 (dp) 换根即可。

    然后我爆栈了。
    由上面不难看出,每个非叶结点会对答案有 ( imes 2) 的贡献;
    设有 (k) 个叶子,考虑根是否为叶子,(ans = (n - k) 2 ^ {n - k} + k 2 ^ {n - k + 1})

    (Source)

    太占位置,不贴了。

    T2、科技

    题目链接

    (Description)

    杨先生是另一名华中科技大学的大一新生。
    与严先生一样,杨先生也是一名联创团队的优秀人才。作为 Web 组的新人,杨先生正在研究服务器的问题。

    杨先生发现,团队内部总共有 (n) 台服务器,通过 (n - 1) 条数据链路进行连接。换句话说,服务器链接成了一个树的形状。每台服务器分配的任务不一样,因此第 (i) 台服务器有一个权值 (v_i),用于描述服务器的重要程度。

    现在杨先生正在进行熬夜测试,他的任务是给每一个服务器确定一个负载值 (w_i) ((w_i leq 1)) 和一个颜色 (黑或者白),使得对于每一个节点 (u),它的子树内的和它同色的节点负载值之和(包括它本身)与它的权值相等

    杨先生想知道熬夜测试的出题人有没有在刁难他,因此你需要告诉杨先生是否存在这么一个方案。如果存在输出"ICHOOSEUNIQUE",不存在输出"ICHOOSEBINGYAN"(均不含引号)。
    当然啦,为了防止你骗分,杨先生对本题采用捆绑测试。

    (n leq 1000 , v_i leq 5000)

    (Solution)

    考虑一棵 (u) 为根的子树,当然希望子树里和 (u) 异色的负载和最小;
    (f_{u}) 表示 (u) 的子树中,与 (u) 异色点负载和的最小值。
    (dp) 即可。
    (g_{i, j}) 表示 (u) 的前 (i) 个子树与 (u) 同色点的负载和为 (j) 的最小异色负载和。
    转移讨论某个儿子与 (u) 是否异色即可:

    [g_{i,j} = min egin{cases} min {g_{i, j - val_v} + f_v}\ \ min {g_{i, j - f_v} + val_v}\ end{cases} \ \ f_{u} = min_{j = 0}^{val_u} {g_{k, j}} ]

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 1e3 + 5, M = 5e3 + 5, inf = 0x3f3f3f3f;
    
    struct edge {
        int next, to;
    } e[N << 1];
    int ecnt = 1, head[N];
    int n, val[N];
    int f[M];
    
    void add_edge(const int u, const int v) {
        e[++ecnt] = (edge){head[u], v}, head[u] = ecnt;
    }
    
    int t[M], s[M];
    void dfs(const int u) {
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            dfs(v);
        }
        memset(s, inf, sizeof(s));
        s[0] = 0;
        for (int i = head[u]; i; i = e[i].next) {
            memset(t, inf, sizeof(t));
            int v = e[i].to;
            for (int j = val[u]; j >= 0; --j) {
                if (j >= val[v])
                    chk_min(t[j], s[j - val[v]] + f[v]);
                if (j >= f[v])
                    chk_min(t[j], s[j - f[v]] + val[v]);
            }
            std::swap(s, t);
        }
        for (int j = val[u]; j >= 0; --j)
            chk_min(f[u], s[j]);
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("sciandtech.in", "r", stdin);
        freopen("sciandtech.out", "w", stdout);
        n = in();
        for (int i = 2, fa; i <= n; ++i) {
            fa = in();
            add_edge(fa, i);
        }
        for (int i = 1; i <= n; ++i)
            val[i] = in();
        memset(f, inf, sizeof(f));
        dfs(1);
        puts((f[1] < inf) ? "ICHOOSEUNIQUE" : "ICHOOSEBINGYAN");
        return 0;
    }
    

    T3、大学

    题目链接

    (Description)

    鸡先生也是一名华中科技大学的大一新生。
    和大家预想的不太一样的是,鸡先生并没有加入联创团队,也不喜欢唱跳和球。

    鸡先生正在准备冰岩作坊游戏组的实习任务,他现在遇到了一个棘手的问题。
    鸡先生要构造出一个地图种子,具体来说,这是一个长度为 (n)(01) 串 。然而鸡先生想要构造出更多的地图种子,因此鸡先生给出了 (m) 个区间 ([L_i,R_i]) ,鸡先生可以对区间内的数进行重排。
    每个区间只能操作一次,并且区间操作需要按照顺序依次进行。
    (m) 个区间内的数全部重排之后,鸡先生想知道,这样可以得到多少个不同的 (01) 串?

    为了方便你处理,鸡先生向你保证,这些区间的 (L_i) 是单调不降的,即保证了 (L_ileq L_{i+1})
    由于答案有点大,因此你需要对 (10^9+7) (一个质数)取模。

    (1leq n,m leq 2000)

    (Solution)

    (0,1) 是对偶的,不妨只关注 (1)

    重新理解一下重排:
    题目要求重排,不妨不去思考如何将区间内的 (0,1) 进行分配,考虑每个位置放什么数。

    不难想到一个 (dp)
    (f_{i,j}) 表示前 (i) 个位置,放了 (j)(1) 的方案数,但此时能用的 (1)(r_i) 之前的所有 (1)
    (其中 (r_i) 表示覆盖到 (i) 的最靠右的右端点)。
    为了方便处理边界,假设每个未被覆盖的位置都是长度为 (1) 的重排区间。

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 2005, mod = 1e9 + 7;
    
    inline void add(int &_, int __) {
        _ += __;
        if (_ >= mod)
            _ -= mod;
    }
    
    int n, m, r[N];
    int f[N][N], pre[N];
    char s[N];
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("university.in", "r", stdin);
        freopen("university.out", "w", stdout);
        n = in(), m = in();
        scanf(" %s", s + 1);
        for (int i = 1; i <= n; ++i)
            r[i] = i, pre[i] = pre[i - 1] + (s[i] == '1');
        for (int i = 1; i <= m; ++i) {
            int x = in(), y = in();
            for (int j = x; j <= y; ++j)
                chk_max(r[j], y);
        }
        f[0][0] = 1;
        for (int i = 1, cur = 1; i <= n; ++i, cur ^= 1) {
            memset(f[cur], 0, sizeof(f[cur]));
            for (int j = 0; j <= i && j <= pre[r[i]]; ++j) {
                if (pre[r[i]] - j > r[i] - i)
                    continue;
                f[cur][j] = f[cur ^ 1][j];
                if (j) {
                    add(f[cur][j], f[cur ^ 1][j - 1]);
                }
            }
        }
        printf("%d
    ", f[n & 1][pre[n]]);
        return 0;
    }
    
  • 相关阅读:
    UITableView的简单使用
    使用UIScrollView 实现分页功能
    UIScrollView的简单使用
    关于行和列的算法
    block的定义和使用
    plist文件的读取和NSBundle的使用
    UIView的transform属性值详解
    JavaScript _proto_、prototype原型、原型链、constructor构造器、类式继承、原型继承
    javascript 跨域问题
    【javascript知识点】javascript 额外篇
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/11813406.html
Copyright © 2011-2022 走看看