牛客练习赛138
A. 小s的签到题
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
for (int i = 0; i < n; ++ i) {
char c;
std::cin >> c;
s += c;
}
char ans = 0;
int max = 0;
for (int i = 0; i < n; ++ i) {
std::string t;
std::cin >> t;
int x = 0;
for (auto & c : t) {
if (c == '/') {
break;
}
x = x * 10 + c - '0';
}
if (x > max) {
ans = s[i];
max = x;
}
}
std::cout << ans << "\n";
}
B. 行列改写
题意:你有长度为\(n\)和长度为\(m\)的数组\(r, c\)。填充一个\(n \times m\)的矩阵,每次选择一个没选择的行或列\(i\),把这一行或这一列都填上\(r_i\)或\(c_i\)。求最后数组的最大元素和。
假设我们从最后一步开始填,那么如果倒数第\(i\)步之前填了\(x\)个行\(y\)个列,那么如果选择第\(i\)行,贡献为\((m - y + 1) \times r_i\),如果选择第\(j\)列贡献为\((n - x + 1) \times c_i\)。显然不管是填行还是列,我们应该选最大的填。假设这两个贡献都能吃满,那么如果填\(r_i\),这一步就损失了\(c_j\)的贡献,如果填\(c_j\),就损失了\(r_i\)的贡献。那么我们应该人损失的贡献最小,也就是\(r_i \geq c_j\)就填\(r_i\)。否则填\(c_j\)。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<i64> a(n), b(m);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < m; ++ i) {
std::cin >> b[i];
}
std::ranges::sort(a, std::greater<>());
std::ranges::sort(b, std::greater<>());
i64 ans = 0;
i64 i = 0, j = 0;
while (i < n && j < m) {
if (a[i] >= b[j]) {
ans += a[i] * (m - j);
++ i;
} else {
ans += b[j] * (n - i);
++ j;
}
}
std::cout << ans << "\n";
}
C. 树上替身追赶游戏
题意:两个人玩游戏,开始都在同一个点,每回合第一个人走到相邻的一个点,然后放置一个假人到这个点相邻的一个点。然后第二个人会往假人的方向走,如果和假人在同一个点就停留。如果任意时刻两个人在同一个点游戏结束。求最大的游戏回合数。
第一个人无法回头,只能一直往下走。那么答案就是距离起点最远的的点的距离加一。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
-- k;
std::vector<std::vector<int>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::queue<int> q;
std::vector<int> d(n, n + 1);
q.push(k);
d[k] = 0;
while (q.size()) {
int u = q.front(); q.pop();
for (auto & v : adj[u]) {
if (d[v] > d[u] + 1) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
int ans = *std::max_element(d.begin(), d.end());
std::cout << ans + 1 << "\n";
}
D. 全对!
题意:\(n\)个长度小于等于\(16\)的\(01\)串。对于一个长度为\(L\)的字符串,如果第\(i\)个位置是\(1\),那么会在第\(k\times L + i\)时刻说好的。其中\(k\)是非负整数。求最小的时刻使得所有字符串都说好的。
把所有相同长度的字符串合到一起,记录\(st_l\)表示长度为\(l\)的所有字符串会在什么时刻一起回答的时刻。这个可以用二进制数存。然后观察到\([1, 16]\)的最小公倍数是\(720720\),那么最小时刻一定小于等于这个时间。那么可以用\(720720 \times 16\)的时间枚举出来哪个时刻所有长度的字符串都说好。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
std::vector<int> st(17), vis(17);
for (int i = 1; i <= 16; ++ i) {
st[i] = (1 << i) - 1;
}
for (int i = 0; i < n; ++ i) {
int len = s[i].size();
vis[len] = 1;
int x = 0;
for (int j = 0; j < len; ++ j) {
if (s[i][j] == '1') {
x += 1 << j;
}
}
st[len] &= x;
}
for (int i = 1; i <= 16; ++ i) {
if (vis[i] && st[i] == 0) {
std::cout << -1 << "\n";
return;
}
}
int m = 1;
for (int i = 1; i <= 16; ++ i) {
if (vis[i]) {
m = std::lcm(m, i);
}
}
for (int t = 1; t <= m; ++ t) {
bool flag = true;
for (int i = 1; i <= 16; ++ i) {
if (!vis[i]) {
continue;
}
int p = t % i == 0 ? i - 1 : t % i - 1;
if (~st[i] >> p & 1) {
flag = false;
break;
}
}
if (flag) {
std::cout << t << "\n";
return;
}
}
std::cout << -1 << "\n";
}
E. 图上替身追赶游戏
题意:比\(c\)多了两个条件:一个是两个人可以重合,一个是你可以加一条边,选完假人后第二个人在所有可以选的点里选编号最小的。求有多少加边方案使得两个人可以在某一时刻不重合不相邻。
首先加一条边显然会形成一个环。那么我们肯定要走到这个环里才能实现不相邻。然后手玩一下发现,只有边数为\(4, 5, 6\)的环才能使得不相邻,那么只要求出有多少形成\(4, 5, 6\)长度环的方案就行。
赛时写了个觉得挺对的\(dsu \ on \ tree\),但没过。
实际上树形\(dp\)就行了,记\(f[u][i]\)为\(u\)的子树里距离它\(0/1/2/3/4/5/\)的点的个数。那么只需要一边跑子树,把这棵子树的贡献和前面的子树的贡献算一下就行了,就是拿两条链加一条边形成一个环。对于长度为\(i\)的环,只要算所有\(\sum_{j=1}^{i} f[u][j] \times f[v][i - j]\),注意\(u, v\)之间有一条边要算上,然后\(f[u][i + 1] += f[v][i]\)加上个数。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
-- k;
std::vector<std::vector<int>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector f(n, std::array<i64, 6>{});
i64 ans = 0;
auto dfs = [&](auto & self, int u, int fa) -> void {
f[u][0] = 1;
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
self(self, v, u);
for (int i = 1; i <= 2; ++ i) {
ans += f[u][i] * f[v][2 - i];
}
for (int i = 1; i <= 3; ++ i) {
ans += f[u][i] * f[v][3 - i];
}
for (int i = 1; i <= 4; ++ i) {
ans += f[u][i] * f[v][4 - i];
}
for (int i = 0; i + 1 < 6; ++ i) {
f[u][i + 1] += f[v][i];
}
}
ans += f[u][3] + f[u][4] + f[u][5];
};
dfs(dfs, k, -1);
std::cout << ans << "\n";
}

浙公网安备 33010602011771号