以下是题解. 6/6 比赛时只完成了前三道. # A. Andryusha and Socks n双袜子, 每次取出一只, 如果桌子上没有和它配对的, 就把它放到桌子上, 否则将这双袜子收进衣橱. 问桌上最多同时有多少只袜子. (1≤n≤10^5)
模拟即可, 然而第一次提交时只读入了n个数......
#include <bits/stdc++.h>
#define Rep(i, a, b) for (int i = (a), i##_ = (b); i < i##_; ++i)
#define For(i, a, b) for (int i = (a), i##_ = (b); i <= i##_; ++i)
#define Down(i, a, b) for (int i = (a), i##_ = (b); i >= i##_; --i)
using namespace std;
const int N = 1e5;
bool f[N+1];
int main()
{
int n, cnt = 0, mx = 0;
cin >> n;
Rep (i, 0, 2*n) {
int x;
cin >> x;
if (f[x]) {
f[x] = false;
--cnt;
} else {
f[x] = true;
++cnt;
}
mx = max(mx, cnt);
}
cout << mx << endl;
return 0;
}
B. The Meeting Place Cannot Be Changed
n个朋友在一个数轴位于1~10^9的某些整点上, 每人有自己的最大速率, 可以往任意方向走. 问它们在某点集合的最短时间 (实数范围) 是多少. (2≤n≤60000)
看到答案是实数, 我想是不是得二分. 这是可行的. 每个人的移动范围是[x-vt, x+vt], 如果这些线段有交, 则最短时间不超过t. 单调性由生活情景可得......
WA了两发, 都是输出格式的锅TAT 第一次是cout保留的位数时多时少, 第二次是CF不支持printf输出long double.
#include <bits/stdc++.h>
#define Rep(i, a, b) for (int i = (a), i##_ = (b); i < i##_; ++i)
#define For(i, a, b) for (int i = (a), i##_ = (b); i <= i##_; ++i)
#define Down(i, a, b) for (int i = (a), i##_ = (b); i >= i##_; --i)
using namespace std;
const int N = 6e4;
typedef long double ld;
int n, x[N], v[N];
inline bool check(ld t)
{
ld l = x[0] - v[0]*t, r = x[0] + v[0]*t;
Rep (i, 1, n) {
l = max(l, x[i] - v[i]*t);
r = min(r, x[i] + v[i]*t);
if (l-r > 1e-9)
return false;
}
return true;
}
int main()
{
cin >> n;
Rep (i, 0, n)
cin >> x[i];
Rep (i, 0, n)
cin >> v[i];
ld l = 0, r = 1e9;
while (r-l > 1e-7) {
ld m = (l+r)/2;
if (check(m))
r = m;
else
l = m;
}
printf("%.7lf\n", (double)(l+r)/2);
return 0;
}
C. Andryusha and Colored Balloons
给一棵树的结点染色, 要求若u,v相邻, v,w相邻, 则u,v,w三者不同色, 求最小的颜色数和一种方案. (3≤结点数≤2*10^5)
DFS, 设现在访问u, 父亲是p, u,p的颜色已确定. 贪心地给u的儿子染色 (颜色编号尽量小, 又不与u,p或它的兄弟相同), 递归下去. 时间复杂度
UPDATE 2017.3.8 官方教程学到证明: 设D为最大度数, 则解不会优于(D+1). 可以看出本算法不会使用大于(D+1)的编号: 根至多有D个儿子, 一种颜色禁用; 其他结点至多有(D-1)个儿子, 两种颜色禁用. |
---|
|
# D. Innokenty and a Football League 给一些球队的名称确定缩写, 有plan A和plan B. 如果某球队x选择B, 则不允许其他球队y选择和A(x)同名的A(y) (如果A(x)=B(y), B(y)还是可以选择的). 问是否有解, 如果有, 输出一组. (1≤n≤1000) |
把上面的奇怪限制翻译一下: 如果一些球队的plan A相同, 它们都只能选择plan B. |
写了一个自己以为能过的 |
看了杜教的代码, 这种调整以前的选择的思想怎么似曾相识呢......不就是匈牙利嘛...... |
把球队和名称各作为一个集合, 则本题就是二分图匹配. 边数是 |
2-SAT也是可以的, 把每个球队的选择作为点, 向有plan和它同名的其他球队的另一个plan连边. |
贪心也是可以的 (来自yp同学), 先确定那些只有一种选择的球队, 对于其他的, 迭代, 同样每次寻找只有一种选择的球队. 如果目前所有球队都有两种选择, 现在就随便挑一个球队选A, 因为 (根据题目中的奇怪限制首先对一些球队做出选择后) A是两两不同的. |
UPDATE 2017.3.8 官方教程类似于2-SAT和yp同学的贪心: A相等的球队, 都只能选择B. 如果所有球队的A不等, 或者现在没有A(k)=B(i), 那么得到答案. 否则, 用类BFS过程进行调整. (一个仔细实现的) 时空复杂度
|
# E. Underground Lab 用k条路径覆盖n个点m条边的无向图的每个顶点, 点可被多次覆盖, 要求每条路径中点的数目不超过 |
保证有解是这个问题本身总是有解, 还是数据保证这一点呢? 先看看它的生成树的情况. 2n暴露了一切. 树的欧拉环游经过每条树边恰好两次, 序列中共有(2n-1)个点. 分成k段即可. |
cout 不关闭流同步会TLE. |
|
# F. Axel and Marston in Bitland n个点m条边的有向图, 每条边上标有0或1. 从点1出发, 走这样生成的无穷序列: 0 0 1 01 10 0110 1001 01101001 10010110 ...... |
求最长路径的长度. 如果该长度大于10^18, 输出-1. (1≤n≤500, 0≤m≤2n^2, 允许重边自环, 但是不存在起点, 终点, 标号均相同的两条边) |
很像有限状态自动机. |
序列的构造方式, 又使人联想到倍增算法. 而背景是有向图, 使人想到矩阵乘法. |
设f[t][i][j][k] 为长度在(2^(i-1), 2^i]的, 以t开头的, j->k的最长路径长度, 容易递推计算 (类似于矩阵乘法). 问题的答案也可以直接得到. 但是时间复杂度 |
我猜测要压位 (后来发现题目中有Bitland , 是不是在提示这一点呢? ), 但是不知道怎么搞 (如果只存0-1, 怎么统计答案呢? ) 学习了一下dls的做法. 修改上述定义, 设f[t][i][j][k] 为是否存在以t开头, 走2^i, j到k的路径. 计算出f 之后, 贪心地从高位往低位确定答案: 倒序枚举i, 维护集合S表示目前所有可行的起点, t表示路径开头的数字. |
求邻接矩阵的积, 压位后可以这样做: 设r[i]为第i行的bitset, c[j]为第j行的bitset, r[i], c[j]按位与, P[i][j]=1当且仅当结果非0. 但是有点麻烦, 而且dls的代码中并没有发现类似的步骤 - 全是按位或. |
看来我的思路没放开啊......对于路径i->k->j, 为什么一定要枚举i, j, 而不学学Floyd呢? 如果i->k存在长为l的路径, 则从k出发长度为l的路径的终点, 都是从i出发长为2l的路径的终点. |
UPDATE 2017.3.8 这个就是布尔矩阵乘法.
#include <bits/stdc++.h>
#define Rep(i, a, b) for (int i = (a), i##_ = (b); i < i##_; ++i)
#define For(i, a, b) for (int i = (a), i##_ = (b); i <= i##_; ++i)
#define Down(i, a, b) for (int i = (a), i##_ = (b); i >= i##_; --i)
using namespace std;
typedef long long ll;
const int N = 500;
bitset<N> f[2][61][N], s;
int main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
Rep (i, 0, m) {
int u, v, t;
cin >> u >> v >> t;
--u;
--v;
f[t][0][u][v] = 1;
}
For (i, 1, 60) {
Rep (u, 0, n) {
Rep (v, 0, n) {
if (f[0][i-1][u][v])
f[0][i][u] |= f[1][i-1][v];
if (f[1][i-1][u][v])
f[1][i][u] |= f[0][i-1][v];
}
}
}
ll ans = 0;
int t = 0;
s[0] = 1;
Down (i, 60, 0) {
bitset<N> tmp;
Rep (u, 0, n)
if (s[u])
tmp |= f[t][i][u];
if (tmp.any()) {
ans |= 1LL<<i;
t ^= 1;
s = tmp;
}
}
cout << (ans > (ll)1e18 ? -1 : ans) << endl;
return 0;
}
本次比赛有四个问题: 1. 太想快速通过A题先发制人, 然而心急吃不了热豆腐, 竞速也不是我擅长的 (看懂英文题面都需要3min). 2. 对于不熟悉的东西, 要谨慎使用, 切忌想当然. 3. 理论上复杂度不对的算法谨慎使用. 4. 做题不一定得按字典序 (不过我读一次题的开销有点大, 需要权衡 -_-b).
yp同学变紫啦......我要努力 QAQ