A - ストーブ (Stove)

有 \(n\) 个客人将要来访,第 \(i\) 个客人的来访时间为 \([a_i, a_i + 1]\),保证 \(\forall i \in [1, n), a_i < a_{i + 1}\)。

在每个客人来访时,你都需要保证暖炉是亮着的(初始时暖炉为熄灭状态)。你可以在任意时刻熄灭暖炉,但每次点亮都需要消耗一根火柴,且你只有 \(k\) 根火柴,求最小的暖炉点亮时间。

\(n \le 10^5, a_i \le 10^9\)。

首先使用一根火柴点亮初始时刻的暖炉,接下来你有 \(k - 1\) 次熄灭暖炉的机会。

考虑在第 \(i\) 个人到第 \(i + 1\) 个人的时间间隙中熄灭暖炉的收益为 \(a_{i + 1} - a_i - 1\),将这个收益排序并取前 \(k\) 大即可。时间复杂度 \(O(n \log n)\)。

Code
#include <iostream>
#include <algorithm> using namespace std; const int N = 1e5 + 5; int n, k;
int a[N], b[N]; int main () {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1; i < n; ++i) {
b[i] = a[i + 1] - a[i] - 1;
}
int ans = a[n] - a[1] + 1;
sort(b + 1, b + n, greater<int>());
for (int i = 1; i < k; ++i) {
ans -= b[i];
}
cout << ans << '\n';
return 0;
}

B - 美術展 (Art Exhibition)

有 \(n\) 件美术品,每件美术品有尺寸 \(A_i\) 与价值 \(B_i\),现在要选择若干美术品作为展览,设选择的美术品集合为 \(S\),最大化 \(\sum\limits_{i \in S} B_i - (\max\limits_{i \in S} A_i - \min\limits_{i \in S} A_i)\)、

\(1 \le n \le 5 \times 10^5, 1 \le A_i \le 10^{15}, 1 \le B_i \le 10^9\)。

首先将所有美术品按照 \(A_i\) 排序,由于所有美术品价值为正,多选一定更优,所以选择的美术品是一段区间。

对 \(B\) 做前缀和。设选择的区间为 \([l, r]\),则价值为 \(B_r - B_{l - 1} - A_r + A_l\)。考虑设一个左端点 \(l\) 的权值为 \(A_l - B_{l - 1}\),设一个右端点 \(r\) 的权值为 \(B_r - A_r\),线性扫一遍即可。时间复杂度 \(O(n \log n)\)。

Code
#include <iostream>
#include <algorithm> using namespace std;
using LL = long long;
using Pii = pair<LL, LL>; const int N = 5e5 + 5; int n;
Pii a[N]; int main () {
cin.tie(0)->sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i].first >> a[i].second;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++i) {
a[i].second += a[i - 1].second;
}
LL mxl = 0, ans = 0;
for (int i = 1; i <= n; ++i) {
mxl = max(mxl, a[i].first - a[i - 1].second);
ans = max(ans, mxl + a[i].second - a[i].first);
}
cout << ans << '\n';
return 0;
}

C - 団子職人 (Dango Maker)

有一个 \(n \times m\) 的网格,每个团子被放在一个格子里,每个团子的颜色是 R, G, 'W' 中的一种。

现在你可以从上到下,或从左到右选一个 \(1 \times 3\) 的矩形,并将其中的团子按顺序做成一个团子串,要求三个团子的颜色必须依次是 R, G, W

求最大的可以制作的团子数。

\(1 \le n, m \le 3 \times 10^3\)。

对于所有团子,考虑在 G 处计算贡献,那么两个团子如果冲突,则它们一定在同一条从左上到右下的对角线上。

所以我们对于每个对角线独立地 \(\text{dp}\)。考虑当前某个对角线的一个格子,设 \(f_0\) 为该团子不做团子串的方案数,\(f_1\) 表示该团子横着串的方案数,\(f_2\) 表示该团子竖着串的方案数。则 \(f_1\) 和 \(f_2\) 之间不能相互转移。

时间复杂度 \(O(nm)\)。

Code
#include <iostream>

using namespace std;

const int N = 3e3 + 5; 

int n, m, f[3], g[3];
char s[N][N]; int main () {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> s[i][j];
}
}
int ans = 0;
for (int sum = 2; sum <= n + m; ++sum) {
f[0] = 0, f[1] = f[2] = -N;
for (int i = max(1, sum - m), j = sum - i; i <= n && j; ++i, --j) {
copy(f, f + 3, g);
fill(f, f + 3, -N);
f[0] = max(max(g[0], g[1]), g[2]);
if (s[i][j] == 'G' && s[i - 1][j] == 'R' && s[i + 1][j] == 'W') {
f[1] = max(g[0], g[1]) + 1;
}
if (s[i][j] == 'G' && s[i][j - 1] == 'R' && s[i][j + 1] == 'W') {
f[2] = max(g[0], g[2]) + 1;
}
}
ans += max(max(f[0], f[1]), f[2]);
}
cout << ans << '\n';
return 0;
}

D - 定期券 (Commuter Pass)

给定一张 \(n\) 个结点 \(m\) 条边的无向图,边有边权。

现在你可以选择一条 \(s\) 到 \(t\) 的最短路,并将这条路径上的边的权值全部设为 \(1\),求操作后 \(u\) 到 \(v\) 最短路的最小值。

\(1 \le n \le 10^5, 1 \le m \le 2 \times 10^5\)。

首先考虑如何刻画“\(s\) 到 \(t\) 的最短路”,我们从 \(s\) 开始跑一遍 \(\text{Dijkstra}\),求出 \(d_i\) 表示 \(s\) 到 \(i\) 的最短路,对于原图上的无向边拆成两条有向边,如果一条有向边 \((u, v)\) 不满足 \(d_v = d_u + w(u, v)\),则将其从图中删去。那么我们将得到一张 \(\text{DAG}\),此时在新图上每一条 \(s\) 到 \(t\) 的路径都是最短路。

然后我们发现答案要么不经过修改权值的边,此时就是 \(dis(u, v)\),要么形如 \(u \rightarrow a \rightarrow b \rightarrow v\),其中 \(a\) 和 \(b\) 在 \(s\) 到 \(t\) 的一条最短路上。则此时答案为 \(dis(u, a) + dis(v, b)\) 或 \(dis(u, b) + dis(v, a)\)。

考虑在 \(\text{DAG}\) 上 \(\text{dp}\),设 \(r_i\) 表示 \(i\) 是否能到 \(t\),\(f_{i, 0}\) 表示 \(i\) 能到达的,且满足 \(r_x = 1\) 的点中最小的 \(dis(v, x)\),\(f_{i, 1}\) 类似,直接 \(\text{dp}\) 即可。对于所有 \(i\),\(dis(s, i), dis(t, i)\) 可以 \(\text{Dijkstra}\) 预处理。

时间复杂度 \(O(n \log n)\)。

Code
#include <iostream>
#include <vector>
#include <queue> using namespace std;
using LL = long long;
using Pii = pair<LL, LL>; const int N = 1e5 + 5;
const LL Inf = 1e18; int n, m, gs, gt, s, t, deg[N];
vector<Pii> e[N];
vector<int> g[N];
LL dis[3][N], f[N][2];
bool r[N]; void Dijkstra (int st, LL *dis) {
priority_queue<Pii, vector<Pii>, greater<Pii>> q;
q.push({0, st});
fill(dis + 1, dis + n + 1, Inf);
while (!q.empty()) {
Pii tp = q.top();
q.pop();
LL d = tp.first;
int u = tp.second;
if (d < dis[u]) {
dis[u] = d;
for (auto i : e[u]) {
int v = i.first, w = i.second;
q.push({d + w, v});
}
}
}
} int main () {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m >> gs >> gt >> s >> t;
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
Dijkstra(s, dis[0]), Dijkstra(t, dis[1]), Dijkstra(gs, dis[2]);
for (int i = 1; i <= n; ++i) {
for (auto j : e[i]) {
if (dis[2][j.first] == dis[2][i] + j.second) {
g[j.first].push_back(i), ++deg[i];
}
}
}
queue<int> q;
for (int i = 1; i <= n; ++i) {
if (!deg[i]) {
q.push(i);
}
}
fill(f[1], f[n] + 2, Inf);
LL ans = dis[0][t];
for (; !q.empty(); q.pop()) {
int u = q.front();
if (u == gt) {
r[u] = 1;
}
if (r[u]) {
f[u][0] = min(f[u][0], dis[1][u]);
f[u][1] = min(f[u][1], dis[0][u]);
ans = min(ans, min(dis[0][u] + f[u][0], dis[1][u] + f[u][1]));
}
for (auto v : g[u]) {
if (r[u]) {
f[v][0] = min(f[v][0], f[u][0]);
f[v][1] = min(f[v][1], f[u][1]);
r[v] = 1;
}
if (!--deg[v]) {
q.push(v);
}
}
}
cout << ans << '\n';
return 0;
}

E - 毒蛇の脱走 (Snake Escaping)

给定 \(n\),对于一个长度为 \(n\) 的二进制数 \(x\),定义其权值为 \(a_x\)。接下来进行 \(q\) 次询问,每次询问格式如下:

  • 给定一个长度为 \(n\) 字符串 \(s\),\(s\) 中的每个字符是 \(\texttt{0, 1, ?}\) 三者之一,对于所有将 ? 填写成 01 的方案组成的数字,求它们的权值和,此处允许前导 \(\texttt{0}\)。

\(1 \le n \le 20, 1 \le q \le 10^6\)。

记 \(s_0, s_1, s_2\) 分别为 \(\texttt{0, 1, ?}\) 在 \(s\) 中的出现位置,\(cnt_0 = |s_0|, cnt_1 = |s_1|, cnt_2 = |s_2|\) 分别为这三种字符在原串中的出现次数。

考虑类似根号分治,因为 \(cnt_0 + cnt_1 + cnt_2 \le 20\),所以 \(\min(cnt_0, cnt_1, cnt_2) \le 6\)。我们 \(\forall i \in [0, 2]\),对于 \(cnt_i \le 6\) 的情况分别处理。


对于 \(cnt_2 \le 6\)

由于 \(\texttt{?}\) 不超过 \(6\) 个,所以我们枚举每个 \(\texttt{?}\) 填什么,对于所有可能的方案求和即可。

时间复杂度 \(O(2^{cnt_2})\)。

对于 \(cnt_0 \le 6\)

考虑只有 \(\texttt{1}\) 和 \(\texttt{?}\) 时怎么做,我们将 \(\text{?}\) 看成 \(\texttt{0}\),那么所有方案的权值和相当于高维后缀和。具体地,设 \(f_s = \sum\limits_{t \supseteq s} a_t\),则一个转化后的集合 \(s\) 权值和为 \(f_s\)。

现在加入 \(\texttt{0}\),由于 \(\texttt{0}\) 的个数很少,所以我们对 \(\texttt{0}\) 容斥,它可以看作 \(\texttt{?}\) 的方案数减去 \(\texttt{1}\) 的方案数,所以我们枚举每个 \(\texttt{0}\) 是变成 \(\texttt{1}\) 还是 \(\texttt{?}\),得到答案为

\[
\sum\limits_{t \subseteq s_0} f_{s_1 \cup t} (-1)^{|t|}

\]

时间复杂度 \(O(2^{cnt_0})\)

对于 \(cnt_1 \le 6\)

类似地考虑只有 \(\texttt{1}\) 和 \(\texttt{?}\) 的情况,我们将 \(\texttt{?}\) 看成 \(\texttt{1}\),设 \(g_s = \sum\limits_{t \subseteq s} a_t\),则一个转化后的集合 \(s\) 权值和为 \(g_{A - s}\),其中 \(A\) 为全集。

还是类似地将 \(\texttt{1}\) 容斥为 \(\texttt{?}\) 的方案减去 \(\texttt{0}\) 的方案,枚举每个 \(\texttt{1}\) 变 \(\texttt{0}\) 还是变 \(\texttt{?}\),可得答案为

\[
\sum\limits_{t \subseteq s_1} g_{A - (s_0 \cup t)}(-1)^{|t|}

\]

时间复杂度 \(O(2^{cnt_1})\)。


最后 \(f, g\) 分别相当于高维后、前缀和,直接上 \(\text{And-FWT}\) 和 \(\text{Or-FWT}\) 即可。

总时间复杂度 \(O(2^nn + q 2^{\lfloor \frac{n}{3} \rfloor})\)。

Code
#include <iostream>

using namespace std;

const int N = 20, S = (1 << N); 

int n, q, all;
int a[S], f[S], g[S]; void FWT_And (int *a) {
for (int k = 1; k <= all; k <<= 1) {
for (int i = 0; i <= all; i += (k << 1)) {
for (int j = 0; j < k; ++j) {
a[i + j] += a[i + j + k];
}
}
}
} void FWT_Or (int *a) {
for (int k = 1; k <= all; k <<= 1) {
for (int i = 0; i <= all; i += (k << 1)) {
for (int j = 0; j < k; ++j) {
a[i + j + k] += a[i + j];
}
}
}
} int main () {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> q, all = (1 << n) - 1;
for (int s = 0; s <= all; ++s) {
char c;
cin >> c;
a[s] = f[s] = g[s] = c - '0';
}
FWT_And(f), FWT_Or(g);
for (string str; q--; ) {
cin >> str;
int s[3] = {0, 0, 0}, cnt[3];
for (int i = 0; i < n; ++i) {
if (str[i] == '0')
s[0] |= (1 << (n - i - 1));
else if (str[i] == '1')
s[1] |= (1 << (n - i - 1));
else if (str[i] == '?')
s[2] |= (1 << (n - i - 1));
}
for (int i = 0; i < 3; ++i) {
cnt[i] = __builtin_popcount(s[i]);
}
if (cnt[2] <= 6) {
int ans = 0;
for (int t = s[2]; ; t = (t - 1) & s[2]) {
ans += a[t | s[1]];
if (!t) break;
}
cout << ans << '\n';
}
else if (cnt[0] <= 6) {
int ans = 0;
for (int t = s[0]; ; t = (t - 1) & s[0]) {
ans += f[t | s[1]] * (__builtin_popcount(t) & 1 ? -1 : 1);
if (!t) break;
}
cout << ans << '\n';
}
else if (cnt[1] <= 6) {
int ans = 0;
for (int t = s[1]; ; t = (t - 1) & s[1]) {
ans += g[(t | s[0]) ^ all] * (__builtin_popcount(t) & 1 ? -1 : 1);
if (!t) break;
}
cout << ans << '\n';
}
}
return 0;
}

JOI 2018 Final的更多相关文章

  1. JOI 2018 Final 题解

    题目列表:https://loj.ac/problems/search?keyword=JOI+2018+Final T1 寒冬暖炉 贪心 暴力考虑每相邻两个人之间的间隔,从小到大选取即可 #incl ...

  2. LOJ#2351. 「JOI 2018 Final」毒蛇越狱

    LOJ#2351. 「JOI 2018 Final」毒蛇越狱 https://loj.ac/problem/2351 分析: 首先有\(2^{|?|}\)的暴力非常好做. 观察到\(min(|1|,| ...

  3. [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分

    题目链接: [JOI 2019 Final]独特的城市 对于每个点,它的答案最大就是与它距离最远的点的距离. 而如果与它距离为$x$的点有大于等于两个,那么与它距离小于等于$x$的点都不会被计入答案. ...

  4. Yandex.Algorithm 2018, final round

    Yandex.Algorithm 2018, final round A Smart Vending B LIS vs. LDS C Eat And Walk D Search Engine E Gu ...

  5. 「JOI 2017 Final」JOIOI 王国

    「JOI 2017 Final」JOIOI 王国 题目描述 题目译自 JOI 2017 Final T3「 JOIOI 王国 / The Kingdom of JOIOI」 JOIOI 王国是一个 H ...

  6. JOI 2019 Final合集

    JOI 2019 Final 合集 #3010. 「JOI 2019 Final」勇者比太郎 其实如果读懂题了就是水题了 题目就是让你求满足条件的\(JOI​\),使得\(O​\)在\(J​\)同行的 ...

  7. 【LOJ】#3014. 「JOI 2019 Final」独特的城市(长链剖分)

    LOJ#3014. 「JOI 2019 Final」独特的城市(长链剖分) 显然我们画一条直径,容易发现被统计的只可能是直径某个距离较远的端点到这个点的路径上的值 用一个栈统计可以被统计的点,然后我们 ...

  8. 【题解】LOJ2759. 「JOI 2014 Final」飞天鼠(最短路)

    [题解]LOJ2759. 「JOI 2014 Final」飞天鼠(最短路) 考虑最终答案的构成,一定是由很多飞行+一些上升+一些下降构成. 由于在任何一个点上升或者下降代价是一样的,所以: 对于上升操 ...

  9. Lyft Level 5 Challenge 2018 - Final Round (Open Div. 2) (前三题题解)

    这场比赛好毒瘤哇,看第四题好像是中国人出的,怕不是dllxl出的. 第四道什么鬼,互动题不说,花了四十五分钟看懂题目,都想砸电脑了.然后发现不会,互动题从来没做过. 不过这次新号上蓝名了(我才不告诉你 ...

  10. 「JOI 2014 Final」飞天鼠

    「JOI 2014 Final」飞天鼠 显然向上爬是没有必要的,除非会下降到地面以下,才提高到刚好为0. 到达一个点有两种情况:到达高度为0和不为0. 对于高度不为0的情况,显然花费的时间越少高度越高 ...

随机推荐

  1. 外网的一个还不错的高性能计算教程: High Performance Computing

    地址: https://info.gwdg.de/wiki/doku.php?id=wiki:hpc:start =========================================== ...

  2. Redis源码安装(Linux环境)

    下载源码: wget https://download.redis.io/redis-stable.tar.gz 解压: tar -xzvf redis-stable.tar.gz 编译&安装 ...

  3. 一些八股:1.fetch 的理解。2.let、const、var

    一. 说说你对 Fetch 的理解,它有哪些优点和不足? Fetch API 是现代 JavaScript 中用于进行网络请求的接口,旨在替代传统的 XMLHttpRequest.它提供了一种更简单. ...

  4. BST二叉查找树的接口设计

    /*************************************************************************************************** ...

  5. shell脚本中$0 $1 $# $@ $* $? $$ 的各种符号意义详解

    一.概述 shell中有两类字符:普通字符.元字符. 1. 普通字符 在Shell中除了本身的字面意思外没有其他特殊意义,即普通纯文本: 2. 元字符 是Shell的保留字符,在Shell中有着特殊的 ...

  6. 【CMake系列】10-cmake测试集成googletest与第三方库自动化构建

    cmake测试,使用ctest 可能不能满足我们的需求,需要我们使用更为强大的第三方测试框架,如googletest,完成项目中的测试工作 本篇文章将第三方测试框架 googletest,引入,同时也 ...

  7. MySql 字段类型长度问题理解

    mysql中字段长度理解 字符长度 设计表中设置的是字符长度,任意字符都占一个字符长度,使用char_length 函数获取 char_length(`name`) 字节长度 字节长度和数据表的字符集 ...

  8. 为什么重写hashCode一定也要重写equals方法?

    这是一个经典的问题,我们先从==开始看起 == "==" 是运算符 如果比较的对象是基本数据类型,则比较的是其存储的值是否相等: 如果比较的是引用数据类型,则比较的是所指向对象的地 ...

  9. k8s pod挂载hostPath执行写时报错Permission denied

    关于hostPath的权限说明 最近项目中经常遇到pod中container挂载主机hostPath报错无权限问题: httpd@hostpath-volume:/test-volume$ touch ...

  10. 【YashanDB知识库】数据库使用shutdown immediate无响应导致coredump

    [标题]数据库使用shutdown immediate无响应导致coredump [问题分类]数据库维护 [关键词]YashanDB, shutdown immediate, coredump [问题 ...