2024牛客暑期多校训练营3
B Crash Test
思路:
a的任意倍数与b的任意倍数的和 都是 a和b的最大公约数的倍数
如果选一次操作,最近的距离为min(D % h[i],h[i] - D % h[i])
那么多次操作的最近距离即为min(D % gcd_h,gcd_h - D % gcd_h)
void solve() {
int n, D;
cin >> n >> D;
int h = 0;
for (int i = 1; i <= n; ++i) {
int x;
cin >> x;
h = gcd(x, h);
}
cout << min(D % h, h - D % h);
}
L Sudoku and Minesweeper
思路:
由于不能替换所有数字,那就只存在一个数字即可,发现一定可以找到一个8满足条件
void solve() {
vector<string> ve(10);
for (int i = 1; i <= 9; ++i) {
cin >> ve[i];
ve[i] = ' ' + ve[i];
}
int x = 0, y = 0;
for (int i = 2; i <= 8; ++i) {
for (int j = 2; j <= 8; ++j) {
if (ve[i][j] == '8') {
x = i, y = j;
break;
}
}
if (x && y) break;
}
for (int i = 1; i <= 9; ++i) {
for (int j = 1; j <= 9; ++j) {
if (i == x && j == y) cout << ve[i][j];
else cout << '*';
}
cout << '\n';
}
}
A Bridging the Gap 2
思路:
首先计算运完所有人需要往返的次数s = (n - r)/ (r - l) (上取整),一次往返需要l个人,所以需要s*l个人来往返
并且可以求出每个人可以往返的次数为(h - 1)/ 2
同时一个人往返的次数不能超过s
那么可以求出所有人的往返次数总和
通过比较所有人的往返次数总和与需要的往返次数s*l来判断是否可以
void solve() {
int n, l, r;
cin >> n >> l >> r;
int s = (n - r + r - l - 1) / (r - l);
int sum = 0;
for (int i = 1; i <= n; ++i) {
int h;
cin >> h;
h --;
h /= 2;
sum += min(h, s);
}
if (sum >= s * l) {
cout << "Yes";
} else cout << "No";
}
D Dominoes!
思路:
首先可以将骨牌按x和y是否相同分为两类
对于x和y不相同的情况,当有一个骨牌x1和y1,一个骨牌x2和y2,若y1等于x2,那么就交换x2和y2,并且y2一定不等于y1,以此方法可以对所有x不等于y的骨牌进行排列
接下来就是对x和y相同的情况,对于两个不同的骨牌xx和yy,可以将其合并为xxyy,那么就可以看成是x和y不相同的骨牌,以此方法对所有x和y相同的骨牌进行合并,最后最多只会剩下一种骨牌
如果最后还剩下一种骨牌,那么考虑把骨牌塞进已有有排列骨牌里,只要当前塞的位置前后骨牌与其不相等就塞,若最后还没塞完那就不存在解
这里已有的骨牌序列里相邻的骨牌都不相等,与剩下的这种骨牌一样的骨牌相邻的位置一定都不能塞,那么就与顺序没有关系,所以直接往里塞来判断的方法是可行的
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e5 + 5, mod = 998244353, Mod = 1e9 + 7, inf = 1e18;
struct E {
int l, r;
};
void solve() {
int n;
cin >> n;
map<int, int> cnt;
vector<E> ve;
for (int i = 1; i <= n; ++i) {
int x, y;
cin >> x >> y;
if (x == y) {
cnt[x] ++;
}
else {
if(!ve.empty() && x == ve.back().r) swap(x, y);
ve.push_back({x, y});
}
}
priority_queue<PII> q;
for (auto [x, y]:cnt) {
q.push({y, x});
}
while (q.size() >= 2) {
auto [c, x] = q.top();
q.pop();
auto [c1, x1] = q.top();
q.pop();
if (!ve.empty() && ve.back().r == x) ve.push_back({x1, x1}), ve.push_back({x, x});
else ve.push_back({x, x}), ve.push_back({x1, x1});
c -- , c1 --;
if (c > 0) q.push({c, x});
if (c1 > 0) q.push({c1, x1});
}
int num = 0, id;
if (q.size()) {
auto [c, x] = q.top();
q.pop();
num = c, id = x;
}
// cerr << num << ' ' << id << '\n';
vector<E> ans;
if (num > 0 && (ve.empty() || (!ve.empty() && ve.front().l != id))) ans.push_back({id, id}), num --;
for (int i = 0; i < ve.size(); ++i) {
ans.push_back(ve[i]);
if (num > 0 && i < ve.size() - 1 && ve[i].r != id && ve[i + 1].l != id) {
ans.push_back({id, id});
num --;
}
}
if (num > 0 && !ve.empty() && ve.back().r != id) {
ans.push_back({id, id}), num --;
}
// if (ans.size() == n) {
cout << "Yes\n";
for (auto [x, y]:ans) cout << x << ' ' << y << '\n';
// } else cout << "No\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
J Rigged Games
思路:
首先对于每个a小局,可以用循环双指针O(n)的预处理出每个起点赢得一个小局的位置,并且是谁赢
对于每个b大局,由于从每个起点开始完成一个小局后的位置已知,可以通过O(n)的知道当前起点开始比赛的赢家,那么求所有的起点为O(n2),考虑优化
可以用倍增来做
to[i][j]表示从i开始进行2j小局后,进行下一小局的位置
cnt[i][j][0/1]表示从i开始进行了2j小局中,0/1赢得的大局数
预处理出to和cnt后,类似于倍增求lca,找到最远的位置,满足0和1赢得的大局数都小于b,那么下一小局将决定谁赢
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e5 + 5, mod = 998244353, Mod = 1e9 + 7, inf = 1e18;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
string s;
int n, a, b;
int to[N][33], cnt[N][33][2];
void solve() {
cin >> n >> a >> b;
cin >> s;
int l = 0, r = 0;
int now[2] = {0, 0};
now[s[r] - '0'] ++;
while (l < n) {
if (now[0] >= a || now[1] >= a) {
if (now[0] >= a) {
cnt[l][0][0] = 1;
} else {
cnt[l][0][1] = 1;
}
to[l][0] = r + 1;
now[s[l++] - '0'] --;
continue;
}
r++;
r %= n;
now[s[r] - '0'] ++;
}
for (int i = 1; i <= 30; ++i) {
for (int j = 0; j < n; ++j) {
to[j][i] = to[to[j][i - 1]][i - 1];
cnt[j][i][0] = cnt[j][i - 1][0] + cnt[to[j][i - 1]][i - 1][0];
cnt[j][i][1] = cnt[j][i - 1][1] + cnt[to[j][i - 1]][i - 1][1];
}
}
for (int i = 0; i < n; ++i) {
now[0] = now[1] = 0;
int pos = i;
for (int j = 30; j >= 0; --j) {
if (now[0] + cnt[pos][j][0] < b && now[1] + cnt[pos][j][1] < b) {
now[0] += cnt[pos][j][0], now[1] += cnt[pos][j][1];
pos = to[pos][j];
}
}
cout << cnt[pos][0][1];
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}