2023年多校联训NOIP层测试2

· · 个人记录

离谱场。

T1 斐波那契树

原:HDU4786 Fibonacci Tree

结论题。

题意:给 N 个点,M 条边,边权只有 0(黑)或 1(白),问能否用这些边组成的生成树各边权之和属于斐波那契数。

思路:因为边权只有 01,所以可以组成的生成树的范围一定介于最小生成树与最大生成树之间。举个例子,已经得到了最小生成树,现在需要找第二小的生成树,那么就把最小生成树中的边权为 1 的边换成 0 就可以得到第二小的,第三小的同理。

int mn = 0, mx = 0;
if(!Krus(n, m, mn)) {
    puts("NO");
    continue;
}
reverse(e + 1, e + 1 + m);
if(!Krus(n, m, mx)) {
    puts("NO");
    continue;
}

for(int i = 1; ; i++)
    if(fib[i] >= mn && fib[i] <= mx) {
        puts("YES");
        break;
    } else if(fib[i] > mx){
        puts("NO");
        break;
    }

T2 期末考试

爆搜题。

题意:共 10 道单选,满分 100,每道题从 ABCD 中选择一个正确选项,选对得 10 分,选错得 0 分,给出 n 份答案及其对应分数,求有多少种可能的正确答案(保证一定有合法答案)。

部分分:

n 份答案中第 i 位共出现了 c_i 种字符,则有:

ans=\prod\limits_{i=1}^{10}(4-c_i)

n 份答案中有 x 份本质不同,则有:

ans = \left \{ \begin{aligned} &30 && x = 1 \\ &2 && x = 2 \\ &1 && \text{otherwise} \end{aligned} \right.
void dfs(int now) {
    if (now > 9) {
        bool OK = true;
        for (int i = 1; i <= n; i++) {
            int _v = 0;
            for (int _ = 0; _ < 10; _++) _v += (s[i][_] == ans[_]) * 10;
            if (_v != v[i]) { OK = false; break; }
        }
        cnt += OK;
        return;
    }
    for (int i = 0; i < 4; i++) {
        ans[now] = 'A' + i;
        dfs(now + 1);
    }
}

T3 麻烦的工作

如题。

题意:有 n 项任务,第 i 项任务需要 a_i 个不同的员工共同完成。共有 m 个员工,第 i 个员工每天只能做 b_i 项任务。现进行 q 次调整(改变原数据),具体有如下四种情况:

  1. a_i\gets a_i+1
  2. a_i\gets a_i-1
  3. b_i\gets b_i+1
  4. b_i\gets b_i-1

求每次调整后的任务是否可以在一天内完成。

- 贪心,遍历 $b_i$,用优先队列维护每项任务当前需要的人数,把能做的任务人数 $-1$ 再塞回去,注意不能重复做同一项任务。 ```cpp while (q--) { while (!Q.empty()) Q.pop(); int op, i; cin >> op >> i; op == 1 ? a[i]++ : op == 2 ? a[i]-- : op == 3 ? b[i]++ : b[i]--; for (int i = 1; i <= n; i++) Q.push(a[i]); for (int i = 1; i <= m; i++) { for (int _ = 1; _ <= min(b[i], n); _++) { int mx = Q.top(); Q.pop(); st[++top] = mx - 1; } while (top) Q.push(st[top--]); if (Q.top() == 0) break; } puts(Q.top() ? "0" : "1"); } ``` - 网络流 ```cpp T = n + m + 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) add(i, n + j, 1); for (int i = 1; i <= m; i++) b[i] = tot, add(n + i, T, read()); for (int i = 1; i <= n; i++) a[i] = tot, add(S, i, read()); auto clear = []() { for (int i = 0; i < tot; i += 2) val[i] += val[i ^ 1], val[i ^ 1] = 0; }; auto check = []() { while (bfs()) dfs(S, inf); for (int i = 1; i <= m; i++) if (val[b[i]]) return false; return true; }; cin >> q; while (q--) { int op, x; cin >> op >> x; if (op == 1) val[b[x]]++; if (op == 2) clear(), val[b[x]]--; if (op == 3) val[a[x]]++; if (op == 4) clear(), val[a[x]]--; printf("%d\n", check()); } ``` ## T4 DP题。 题意:给定一颗树,树边有向,可以进行四种操作: 1. 选择:花费 $t_i$ 走到相邻结点。 2. 重开:回到 $1$ 号点,无花费。 3. 存档:设置当前点为存档点,如果之前有存档会被覆盖(只能设置一个存档点),无花费。 4. 读档:回到存档点,无花费。 求访问过所有叶子结点的最小花费。 部分分: - 直接输出所有边权之和有 $20pts$。 - 咕。