今日份模拟赛混搭题解
今日份模拟赛混搭题解
匹配
题目描述
给定一棵 \(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)\) 点对数量:
我们设 \(t1, t2\),使得其所得 \((x, y)\) 一致,那么可以得到:
我们不妨设 \(t1 \le t2\) 且 \(t2 = t1 + x \times B(x \in \mathbb{N} ^ +)\),接下来就是推式子了。
那么 \(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
:
再根据加法原理和乘法原理即可推出方案数。
即
代码
#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;
}
本文来自博客园,作者:xxcxu,转载请注明原文链接:https://www.cnblogs.com/Maraschino/p/16526060.html