今日份模拟赛混搭题解

今日份模拟赛混搭题解

匹配

题目描述

给定一棵 \(n\) 为偶数的树,请将树中的点两两配对,一个配对产生的收益是这两个点的距离。

请给出一种配对,使得总收益最大。

如果有多个方案,输出任意一个均可。

解题思路

考虑如何求树上两点距离。

\(dis_{u, v} = dep_u + dep_v - 2 \times dep_{lca_{u, v}}\)

那么它的深度综合是不会变的,我们只需要让 \(lca\) 的深度最小化即可。

那么深度最小的点肯定就是根节点。

于是考虑每次选择不同子树的两个点,使其 \(lca = root\),答案会最小化。

为了能匹配成功,我们求一个重心,以重心为根。

如何选择一对点?

由于求出了重心,每颗子树的大小都 \(size \leq \dfrac{n}{2}\),我们只需要选择 dfs序 相差 \(\dfrac{n}{2}\) 的两点即可。

代码

#include <bits/stdc++.h>
const int N = 1e5 + 5;
int n, rt, size[N], mx[N], redfn[N];
std::vector<int> Graph[N];
void Findroot(int x, int fa) {
    for (int y : Graph[x]) if (y ^ fa) {
        Findroot(y, x);
        size[x] += size[y];
        mx[x] = std::max(mx[x], size[y]);
    } mx[x] = std::max(mx[x], n - size[x]);
    if (!rt || mx[x] < mx[rt]) rt = x;
}
void Dfs(int x, int fa) {
    static int time_stamp = 0;
    redfn[++time_stamp] = x;
    for (int y : Graph[x]) if (y ^ fa) Dfs(y, x);
}
signed main() {
    scanf("%d", &n);
    for (int i = 1, u, v; i < n; ++i) {
        scanf("%d%d", &u, &v);
        Graph[u].emplace_back(v);
        Graph[v].emplace_back(u);
    } Findroot(1, 0); Dfs(rt, 0);
    for (int i = 1; i <= n / 2; ++i) printf("%d %d\n", redfn[i], redfn[i + n / 2]);
    return 0;
}

[APIO2019] 奇怪装置

题目描述

考古学家发现古代文明留下了一种奇怪的装置。该装置包含两个屏幕,分别显示两个整数 \(x\)\(y\)

经过研究,科学家对该装置得出了一个结论:该装置是一个特殊的时钟,它从过去的某个时间点开始测量经过的时刻数 \(t\),但该装置的创造者却将 \(t\) 用奇怪的方式显示出来。若从该装置开始测量到现在所经过的时刻数为 \(t\),装置会显示两个整数:\(x = ((t + \lfloor \dfrac {t}{B} \rfloor) \mod A)\),与 \(y=(t \mod B)\)。这里 \(\lfloor x \rfloor\) 是下取整函数,表示小于或等于 \(x\) 的最大整数。

考古学家通过进一步研究还发现,该装置的屏幕无法一直工作。实际上,该装置的屏幕只在 \(n\) 个连续的时间区间段中能正常工作。第 \(i\) 个时间段从时刻 \(l_i\) 到时刻 \(r_i\)。现在科学家想要知道有多少个不同的数对 \(x,y\) 能够在该装置工作时被显示出来。

两个数对 \((x_1,y_1)\)\((x_2,y_2)\) 不同当且仅当 \(x_1 \neq x_2\)\(y_1 \neq y_2\)

提示

对于全部数据,\(1 \leq n \leq 10^6,1 \leq A,B \leq 10^{18},0 \leq l_i \leq r_i \leq 10^{18}\)

\(S=\sum_{i=1}^n (r_i-l_i+1)\)\(L=\max_{i=1}^n (r_i-l_i+1)\)

详细子任务附加限制与分值如下表 (注:这里给出的子任务与本题在这里的最终评测无关,仅供参考)

子任务 附加限制 分值
1 \(S\leq 10^6\) 10
2 \(n=1\) 5
3 \(A\times B<=10^6\) 5
4 \(B=1\) 5
5 \(B\leq 3\) 5
6 \(B\leq 10^6\) 20
7 \(L\leq B\) 20
8 无附加限制 30

解题思路

题目要求的是满足下列条件的 \((x, y)\) 点对数量:

\[\begin{cases} x \equiv t + \left\lfloor\dfrac{t}{B}\right\rfloor \pmod{A}\\ y \equiv t \pmod{B} \end{cases}\]

我们设 \(t1, t2\),使得其所得 \((x, y)\) 一致,那么可以得到:

\[\begin{cases} t2 + \left\lfloor\dfrac{t2}{B}\right\rfloor \equiv t1 + \left\lfloor\dfrac{t1}{B}\right\rfloor \pmod{A}\\ t1 \equiv t2 \pmod{B} \end{cases}\]

我们不妨设 \(t1 \le t2\)\(t2 = t1 + x \times B(x \in \mathbb{N} ^ +)\),接下来就是推式子了。

\[t1 + x \times B + \left\lfloor\dfrac{t1 + x \times B}{B}\right\rfloor \equiv t1 + \left\lfloor\dfrac{t1}{B}\right\rfloor \pmod{A} \]

\[t1 + x \times B + \left\lfloor\dfrac{t1}{B} + x\right\rfloor \equiv t1 + \left\lfloor\dfrac{t1}{B}\right\rfloor \pmod{A} \]

\[x \times (B + 1) \equiv 0 \pmod{A} \]

\[\dfrac{A \times B}{gcd(A, B + 1)} \mid x \]

那么 \(t = \dfrac{A \times B}{gcd(A, B + 1)}\) 即是一个循环节。

对于每对 \((l, r)\) 有以下三种情况

  • \(r - l + 1 \geq t\),那么这个时间段就包括了全部的 \((x, y)\)

  • \(L = l \mod t\)\(R = r \mod t\),若是 \(L <= R\),那就将 \([L, R]\) 映射到数轴上,作线段覆盖。

  • \(L > R\),不能直接将 \([L, R]\) 映射,于是分段,分为 \([L, t - 1]\)\([0, R]\) 两条线段,与第二种情况相同处理。

最后我们考虑如何做线段覆盖,直接排序后不断更新右端点,将每段长度统计进入答案。

代码

#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 5;
int n, a, b, ans, cnt;
struct node {int l, r;} segment[N << 1];
signed main() {
    scanf("%lld%lld%lld", &n, &a, &b);
    std::function<int(int, int)> Gcd = [&](int x, int y) {return !y ? x : Gcd(y, x % y);};
    int t = a / Gcd(a, b + 1) * b;
    for (int i = 1, l, r; i <= n; ++i) {
        scanf("%lld%lld", &l, &r);
        if (r - l + 1 >= t) return printf("%lld\n", t), 0;
        else {
            int L = l % t, R = r % t;
            if (L > R) segment[++ cnt] = {0, R}, segment[++ cnt] = {L, t - 1};
            else segment[++ cnt] = {L, R};
        }
    } 
    std::sort(segment + 1, segment + cnt + 1, [&](node a, node b) {return a.l < b.l;});
    int ans = 0, ll = segment[1].l, rr = segment[1].r; segment[++ cnt].l = t + 1;
    for (int i = 2; i <= cnt + 1; ++i) {
        if (rr < segment[i].l) ans += rr - ll + 1, ll = segment[i].l, rr = segment[i].r;
        else rr = std::max(rr, segment[i].r);
    } return printf("%lld\n",ans), 0;
}

士兵的放置

题目描述

\(N\) 个房间和 \(N-1\) 个双向通道,任意两个房间均可到达。为了加强房间的安保,现在如果在某个房间中放一个士兵,则这个房间以及所有与这个房间相连的房间都会被控制。

现在想请你求出至少要多少士兵可以控制所有房间,以及有多少种不同的方案数。

解题思路

裸的最小支配集板子,多了个统计方案数。

\(f_{i,0/1/2}\) 分别表示被自己/儿子/父亲支配的最小点数,\(g_{i,0/1/2}\) 分别表示被自己/儿子/父亲支配时的方案数。

可以简单推出树形 dp

\[f_{x, 0} = \sum_{y \in son_x} \min(f_{y, 0}, f_{y, 1}, f_{y, 2}) \]

\[f_{x, 1} = \min({f_{x, 1} + f_{y, 0}, f_{x, 1} + f_{y, 1}, f_{x, 2} + f_{y, 0}}) \]

\[f_{x, 2} = \min(f_{y, 1}) \]

再根据加法原理和乘法原理即可推出方案数。

\[g_{x, 0} = \prod_{y \in son_x}g_{y, 0}[f_{y, 0} == min] + g_{y, 1}[f_{y, 1} == min] + g_{y, 2}[f_{y, 2} == min] \]

\[g_{x, 1} = (g_{x, 1} \times g_{y, 0})[f_{x, 1} + f_{y, 0} == min] + (g_{x, 1} \times g_{y, 1})[f_{x, 1} + f_{y, 1} == min] + (g_{x, 2} \times g_{y, 0})[f_{x, 2} + f_{y, 0} == min] \]

\[g_{x, 2} = \prod_{y \in son_x}g_{y, 1} \]

代码

#include <bits/stdc++.h>
#define int long long
const int N = 5e5 + 5, INF = N + 1, mod = 1032992941; int n, f[N][3], g[N][5];
std::vector<int> Graph[N];
int add(int x, int y) {return (x + y) % mod;}
int mul(int x, int y) {return 1ll * x * y % mod;}
void Dfs(int x, int fa) {
    f[x][0] = 1, f[x][1] = INF, f[x][2] = 0;
    g[x][0] = g[x][1] = g[x][2] = 1;
    for (auto y : Graph[x]) {
        if (y == fa) continue; Dfs(y, x); int tmp, t = 0;
        tmp = std::min({f[y][0], f[y][1], f[y][2]});
        if (f[y][0] == tmp) t = add(t, g[y][0]);
        if (f[y][1] == tmp) t = add(t, g[y][1]);
        if (f[y][2] == tmp) t = add(t, g[y][2]);
        f[x][0] += tmp; g[x][0] = mul(g[x][0], t);

        t = 0;
        tmp = std::min({f[x][1] + f[y][0], f[x][1] + f[y][1], f[x][2] + f[y][0]});
        if (f[x][1] + f[y][0] == tmp) t = add(t, mul(g[y][0], g[x][1]));
        if (f[x][1] + f[y][1] == tmp) t = add(t, mul(g[y][1], g[x][1]));
        if (f[x][2] + f[y][0] == tmp) t = add(t, mul(g[y][0], g[x][2]));
        f[x][1] = tmp; g[x][1] = t;

        f[x][2] += f[y][1]; g[x][2] = mul(g[x][2], g[y][1]);
    } return void();
}
signed main() {
    scanf("%lld", &n);
    for (int i = 1, u, v; i < n; ++i) {
        scanf("%lld%lld", &u, &v);
        Graph[u].emplace_back(v);
        Graph[v].emplace_back(u);
    } Dfs(1, 0);
    int tmp = std::min(f[1][0], f[1][1]), ans = 0;
    if (tmp == f[1][0]) ans = add(ans, g[1][0]);
    if (tmp == f[1][1]) ans = add(ans, g[1][1]);
    return printf("%lld\n%lld\n", tmp, ans), 0;
}
posted @ 2022-07-27 19:42  xxcxu  阅读(32)  评论(0编辑  收藏  举报