CodeForces Contest #1137: Round #545 (Div. 1)
比赛传送门:CF #1137。
比赛记录:点我。
每次都自闭的 div1 啊,什么时候才能上 IM 呢。
【A】Skyscrapers
题意简述:
有一个 \(n\times m\) 的矩阵 \(a_{ij}\)。
对于每个 \((i,j)\)(\(1\le i\le n\),\(1\le j\le m\)),你把第 \(i\) 行和第 \(j\) 列单独抽出,这样就有 \(n+m-1\) 个数被你抽出。
你可以对这些数重新标号为正整数,但是要满足第 \(i\) 行所有数的大小关系不变,第 \(j\) 列所有数的大小关系不变(两个限制相互独立)。
满足这个限制下,你希望最大的标号尽量小,对每个 \((i,j)\) 求出这个最小的最大标号。
题解:
因为行大小关系不变,列大小关系不变,我们考虑分别离散化每行每列,并统计每个数在行内和列内的排名。
若 \((i,j)\) 在行内排名为第 \(x\) 小,列内排名为第 \(y\) 小,它最优情况下就被标号为 \(\max(x,y)\),然后行内、列内比它大的数依次往大排。
由此写出代码:
#include <cstdio>
#include <algorithm>
const int MN = 1005;
int N, M, A[MN][MN];
int X[MN][MN], Y[MN][MN];
int B[MN], C1[MN], C2[MN], C;
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; ++i)
for (int j = 1; j <= M; ++j)
scanf("%d", &A[i][j]);
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= M; ++j) B[j] = A[i][j];
std::sort(B + 1, B + M + 1), C1[i] = C = std::unique(B + 1, B + M + 1) - B - 1;
for (int j = 1; j <= M; ++j) X[i][j] = std::lower_bound(B + 1, B + C + 1, A[i][j]) - B;
}
for (int j = 1; j <= M; ++j) {
for (int i = 1; i <= N; ++i) B[i] = A[i][j];
std::sort(B + 1, B + N + 1), C2[j] = C = std::unique(B + 1, B + N + 1) - B - 1;
for (int i = 1; i <= N; ++i) Y[i][j] = std::lower_bound(B + 1, B + C + 1, A[i][j]) - B;
}
for (int i = 1; i <= N; ++i, puts(""))
for (int j = 1; j <= M; ++j)
printf("%d ", std::max(X[i][j], Y[i][j]) + std::max(C1[i] - X[i][j], C2[j] - Y[i][j]));
return 0;
}
【B】Camp Schedule
题意简述:
有两个 01 串 \(s\) 和 \(t\),要求重新排列 \(s\) 使得 \(t\) 在重新排列后的 \(s\) 中出现次数尽量多(位置相交的出现也算)。
题解:
贪心匹配 \(t\),随便用什么方法算出 \(t\) 的最长真公共前后缀,我用了 KMP,然后贪心能选就选。
#include <cstdio>
#include <cstring>
const int MN = 500005;
int N, M, c0, c1;
char s[MN], t[MN];
int p[MN];
int main() {
scanf("%s%s", s + 1, t + 1);
N = strlen(s + 1), M = strlen(t + 1);
for (int i = 1; i <= N; ++i)
if (s[i] == '0') ++c0;
else ++c1;
int k = 0;
for (int i = 2; i <= M; ++i) {
while (k && t[k + 1] != t[i]) k = p[k];
if (t[k + 1] == t[i]) p[i] = ++k;
}
k = 0;
for (int i = 1; i <= N; ++i) {
int r = t[k + 1] - '0';
int q = r ? c1 ? 1 : 0 : c0 ? 0 : 1;
if (q == r) if (++k == M) k = p[M];
--(q ? c1 : c0);
printf("%d", q);
}
return 0;
}
【C】Museums Tour
题意简述:
一个 \(n\) 个点 \(m\) 条边的简单有向图,一周有 \(d\) 天。
每个点都有一个博物馆,给出博物馆 \(i\) 在一周的第 \(j\) 天的开门情况(\(1\le i\le n\),\(1\le j\le d\))。
这周的第 \(1\) 天,你从 \(1\) 号点开始,每次走一条边需要花费 \(1\) 天,如果当前点的博物馆开着,你就可以进去游览。
问在足够长的时间后,你最多可以游览多少个不同的博物馆(一个点的博物馆游览多次只算一次)。
题解:
有两种做法,一种比较简单,一种比较复杂,我写的是复杂的。
考虑 \(\mathrm{f}[i][j]\) 表示一周的第 \(j\) 天在 \(i\) 号点,最终能游览多少个博物馆。
但是图上有环,转移比较困难。于是考虑强连通分量缩点。这样形成一个 DAG。
观察可以得到,一个缩点后的强连通分量,是拥有自己的“周期”的,即若第 \(j\) 天在这个强连通分量中,第 \(j+x\) 天也能在同一个点上,最小的正整数 \(x\) 便是周期。
这个周期一定是 \(d\) 的因数,因为 \(d\) 是所有强连通分量的总周期,周期可以这样计算:
周期 \(\mathrm{period}\) 初始化为 \(d\)。
考虑这个强连通分量的 DFS 树,记录每个点的深度 \(\mathrm{dep}\)。
对于每一条边 \(u\to v\),令 \(\mathrm{period}=\gcd(\mathrm{period},\mathrm{dep}[u]-\mathrm{dep}[v]+1)\)。
关于证明,只能感性理解。
这之后就可以 DP 了,DP 的状态变为 \(\mathrm{f}[i][j]\) 表示 \(i\) 号强连通分量中的时间戳为 \(j\) 的答案。
因为涉及到强连通分量内所有点的时间戳,需要对每个强连通分量设置一个基准时间戳以便互相转化,所幸我们可以用 \(\mathrm{dep}\bmod \mathrm{period}\) 来当作节点的相对时间戳,基准时间戳为 \(0\)。
DP 时需要处理一个 \(\mathrm{cnt}[j]\) 数组表示当强连通分量时间戳为 \(j\) 时这个强连通分量内开启的不同博物馆个数。DP 时要格外注意不同强连通分量中时间戳的转换。
时间复杂度 \(\mathcal{O}((n+m)d)\)。
#include <cstdio>
#include <vector>
#include <algorithm>
const int MN = 100005, MD = 55;
int N, M, D;
std::vector<int> G[MN], T[MN];
char s[MD]; int w[MN][MD];
int dfn[MN], low[MN], stk[MN], instk[MN], tp, dfc;
int dep[MN], bel[MN], per[MN], tic[MN], itic[MN], bcnt;
void Tarjan(int u) {
low[u] = dfn[u] = ++dfc;
stk[++tp] = u, instk[u] = 1;
for (auto v : G[u]) {
if (!dfn[v]) {
dep[v] = dep[u] + 1, Tarjan(v);
low[u] = std::min(low[u], low[v]);
}
else if (instk[v]) low[u] = std::min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
++bcnt;
for (int x = 0; x != u; --tp) {
x = stk[tp];
bel[x] = bcnt;
T[bcnt].push_back(x);
instk[x] = 0;
}
}
}
int f[MN][MD];
int main() {
scanf("%d%d%d", &N, &M, &D);
for (int i = 1; i <= M; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
}
for (int i = 1; i <= N; ++i)
if (!dfn[i]) dep[i] = 1, Tarjan(i);
for (int i = 1; i <= bcnt; ++i) per[i] = D;
for (int u = 1; u <= N; ++u)
for (auto v : G[u]) if (bel[u] == bel[v])
per[bel[u]] = std::__gcd(per[bel[u]], std::abs(dep[u] - dep[v] + 1));
for (int i = 1; i <= N; ++i)
tic[i] = dep[i] % per[bel[i]], itic[i] = tic[i] ? per[bel[i]] - tic[i] : 0;
for (int i = 1; i <= N; ++i) {
scanf("%s", s);
for (int j = 0; j < D; ++j) w[i][j] = s[j] - '0';
}
for (int id = 1; id <= bcnt; ++id) {
int pr = per[id];
static int cnt[MD];
for (int j = 0; j < pr; ++j) cnt[j] = 0;
for (int j = 0; j < pr; ++j) {
for (auto u : T[id]) for (int k = 0; k < D; k += pr)
if (w[u][(k + tic[u] + j) % D]) { ++cnt[j]; break; }
}
for (int j = 0; j < pr; ++j) {
f[id][j] = cnt[j];
for (auto u : T[id]) {
int ticv = (j + tic[u] + 1) % pr;
for (auto v : G[u]) if (bel[v] != id) {
for (int k = 0; k < D; k += pr)
f[id][j] = std::max(f[id][j], cnt[j] + f[bel[v]][(itic[v] + k + ticv) % per[bel[v]]]);
}
}
}
}
printf("%d\n", f[bel[1]][itic[1]]);
return 0;
}
// 19-03-10 00:18 ~ 19-03-10 00:54
而相对简单的做法是,将每个点在一周的每天拆成不同的点。这样有 \(n\times d\) 个点,每个点记作 \(\langle u,j\rangle\) 即 \(u\) 号点在第 \(j\) 天拆出的点(\(1\le u\le n\),\(0\le j<d\))。每个点的权值为 \(0\) 或 \(1\),取决于当天此点的博物馆是否开启。
对于一条边 \(u\to v\),将其拆成 \(d\) 条边,连接 \(\langle u,j\rangle\) 和 \(\langle v,(j+1)\bmod d\rangle\)。
这样再强连通分量缩点后,形成一个 DAG。每个缩点后的强连通分量权值为这个强连通分量中开启的不同博物馆个数,直接求最长路即可。
为什么不会重复统计?考虑重复统计的情况,即存在一条路径使得 \(\langle u,j_1\rangle\) 可以到达 \(\langle u,j_2\rangle\) 但不在同一个强连通分量中,这是不可能的,因为单独看这条路径经过的顶点编号序列,沿着这个序列再重复走 \(d-1\) 遍一定能回到 \(\langle u,j_1\rangle\)。这意味着如果同一顶点拆出的两点弱连通,它们必然也强连通,所以不需要担心重复统计的情况。
代码就不给了,比较简单。不过这个做法似乎空间翻好几番,要小心处理。
【D】Cooperative Game
题意简述:
这是一道交互题。
有一张 \(t+c\)(\(1\le t,c\),\(t+c\le 1000\))个点的有向图,每个点只有一条出边。这张图由一个长度为 \(t\) 的链和一个长度为 \(c\) 的环构成。
即从起点(链的顶端)走 \(t-1\) 条边即可访问到链的尾端,从链的尾端再走 \(1\) 条边即可进入环中,记环上的这个点(从链进入的第一个点)为 \(\mathrm{finish}\)。
你在起点(链的顶端)有 \(10\) 枚棋子,每次你可以令一部分棋子走一条边,然后交互库会告诉你哪些棋子在同一个节点内,哪些棋子在不同节点内。
你需要在不超过 \(3(t+c)\) 次操作后让所有棋子到达 \(\mathrm{finish}\) 并结束程序。
题解:
思路非常巧妙的一题。其实只需要 \(3\) 枚棋子即可完成任务。
考虑先移动 \(2\) 枚棋子,类似 Floyd 判圈算法,以及 Pollad-rho 算法的判圈过程,我们令一枚棋子速度为 \(2\),另一枚速度为 \(1\)。
它们一定会在环上相遇,并且相遇时,较慢的棋子还没有走完一整个环,令相遇的点为 \(\mathrm{meet}\)。
假设从 \(\mathrm{finish}\) 开始,走 \(x\) 条边第一次到达 \(\mathrm{meet}\),较慢的棋子走了 \(\mathrm{slow}\) 步,较快的棋子走了 \(\mathrm{fast}\) 步(显然 \(\mathrm{fast}=2\times\mathrm{slow}\)):
有 \(\mathrm{slow}=t+x\) 和 \(2\times\mathrm{slow}=\mathrm{fast}=t+k\times c+x\)(\(k\in\mathbb{Z}^+\)),整理得:
\(2(t+x)=t+k\times c+x\),即 \(t+x=k\times c\),即 \(t\equiv c-x\pmod{c}\)。
\(c-x\) 即为 \(\mathrm{meet}\) 在环上剩余的步数,即从 \(\mathrm{meet}\) 走 \(c-x\) 步又到达 \(\mathrm{finish}\)。
所以有剩余的步数加上若干倍的 \(c\) 等于 \(t\)。也就是说,如果这时开始,我们每次让所有棋子同时走一条边,最终在 \(t\) 步之后就都会到达 \(\mathrm{finish}\),这正是我们要求的。
于是有了下面的代码:
#include <cstdio>
int t;
inline void gett() {
fflush(stdout);
scanf("%d", &t);
for (int x = t; x; --x) scanf("%*s");
}
int main() {
do {
puts("next 0 1"), gett();
puts("next 0"), gett();
} while (t == 3);
do {
puts("next 0 1 2 3 4 5 6 7 8 9"), gett();
} while (t == 2);
puts("done");
return 0;
}
【E】Train Car Selection
咕
CodeForces Contest #1137: Round #545 (Div. 1)的更多相关文章
- CodeForces Contest #1114: Round #538 (Div. 2)
比赛传送门:CF #1114. 比赛记录:点我. 又 FST 了. [A]Got Any Grapes? 题意简述: 有三个人,第一个人需要吃绿色葡萄至少 \(a\) 个,第二个人需要吃绿色和紫色葡萄 ...
- Codeforces Round #545 (Div. 1) 简要题解
这里没有翻译 Codeforces Round #545 (Div. 1) T1 对于每行每列分别离散化,求出大于这个位置的数字的个数即可. # include <bits/stdc++.h&g ...
- Codeforces Round #545 (Div. 2) D 贪心 + kmp
https://codeforces.com/contest/1138/problem/D 题意 两个01串s和t,s中字符能相互交换,问最多能得到多少个(可交叉)的t 题解 即将s中的01塞进t中, ...
- Codeforces Round #545 (Div. 2) D
链接:http://codeforces.com/contest/1138/problem/D 啊啊啊啊啊啊,自闭啊,比赛的时候判断条件 if(s1[i-1]=='0') aa++;写成了 if(s1 ...
- Codeforces Round #545 (Div. 2)(D. Camp Schedule)
题目链接:http://codeforces.com/contest/1138/problem/D 题目大意:给你两个字符串s1和s2(只包含0和1),对于s1中,你可以调换任意两个字符的位置.问你最 ...
- Codeforces Round #545 (Div. 2)(B. Circus)
题目链接:http://codeforces.com/contest/1138/problem/B 题目大意:贼绕口的题目,就是给你两个字符串s1,s2,然后每一个人代表一列,第一列代表技能一每个人是 ...
- Codeforces Round #545 (Div. 2) E 强连通块 + dag上求最大路径 + 将状态看成点建图
https://codeforces.com/contest/1138/problem/E 题意 有n个城市(1e5),有m条单向边(1e5),每一周有d天(50),对于每个城市假如在某一天为1表示这 ...
- Codeforces Round #545 (Div. 2) 交互 + 推公式
https://codeforces.com/contest/1138/problem/F 题意 有一条长为t的链,一个长为c的环,定义终点为链和环相连环上的第一个点,现在有10个人在起点,你每次可以 ...
- Codeforces Round #545 (Div. 1) Solution
人生第一场Div. 1 结果因为想D想太久不晓得Floyd判环法.C不会拆点.E想了个奇奇怪怪的set+堆+一堆乱七八糟的标记的贼难写的做法滚粗了qwq靠手速上分qwqqq A. Skyscraper ...
随机推荐
- BZOJ 4754 [JSOI2016]独特的树叶 | 树哈希判同构
题目链接 这道题是一道判断无根树同构的模板题,判断同构主要的思路就是哈希. 一遇到哈希题,一百个人能有一百零一种哈希方式,这篇题解随便选用了一种--类似杨弋<Hash在信息学竞赛中的一类应用&g ...
- [luogu3834]静态区间第k小【主席树】
传送门:https://www.luogu.org/problemnew/show/P3834 题目描述 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 分析 很多人都说是用 ...
- A1037. Magic Coupon
The magic shop in Mars is offering some magic coupons. Each coupon has an integer N printed on it, m ...
- 11:SSM框架下各个层的解释说明
具体见网址:http://blog.csdn.net/lutianfeiml/article/details/51864160
- 洛谷 P1064 金明的预算方案(01背包问题)
传送门:Problem 1064 https://www.cnblogs.com/violet-acmer/p/9852294.html 题解: 这道题是 “01”背包问题的变形. 如果不考虑买附件必 ...
- C# Winform 按回车键查找下一个可设置焦点的组件
private void frmLogin_KeyPress(object sender, KeyPressEventArgs e) { //按回车键查找下一个可设置焦点的组件. if (e.KeyC ...
- JSONObject的toBean 和 fromObject
public static void main(String[] args) { Map map=new HashMap(); map.put("我","妹") ...
- Angular开发小笔记
一.父组件怎么覆盖子组件的样式呢 1./deep/(不建议这么做,以后angular会取消,因为这样写不利于组件的独立性) 在父组件的scss里面写: :host{ 子组件名 /deep/ label ...
- python3简单实现微信爬虫
Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spm=a1z38n.10677092.0.0.482434a6E ...
- nginx-1.12.1编译参数详情
一下nginx-1.12.1编译参数详情 #/usr/local/nginx/sbin/nginx -V nginx version: nginx/1.12.1 built by gcc 4.4.7 ...