Refact.ai Match 1 (Codeforces Round 985)
A. Set
二分出最大数满足至少有\(k\)个倍数的数。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
void solve() {
int l, r, k;
cin >> l >> r >> k;
int L = l, R = r, res = -1;
while(L <= R) {
int mid = (L + R) / 2;
int cnt = r / mid - (l - 1) / mid;
if(cnt >= k) res = mid, L = mid + 1;
else R = mid - 1;
}
if(res < l) cout << 0 << "\n";
else cout << res - l + 1 << "\n";
return ;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while(T --)
solve();
return 0;
}
有一个结论,这个数实际上就是\(\left\lfloor \frac {r}{k} \right\rfloor\)
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using vi = vector<int>;
using pii = pair<int,int>;
void solve(){
int l, r, k;
cin >> l >> r >> k;
cout << max(0, r / k - l + 1) << "\n";
return ;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while(T --)
solve();
return 0;
}
B. Replacement
这题,相当于每次选择两个相邻且不同数字,然后删掉\(r_i\oplus 1\)。所以只要当前\(0,1\)的个数都大于\(0\)就一定有解。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
void solve() {
int n;
string s, r;
cin >> n >> s >> r;
vi cnt(2);
for(auto i : s)
cnt[i - '0'] ++;
for(int x; auto i : r){
x = (i - '0') ^ 1;
if(cnt[0] > 0 and cnt[1] > 0)
cnt[x] --;
else {
cout << "NO\n";
return;
}
}
cout << "YES\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while(T --)
solve();
return 0;
}
C. New Rating
一个比较神秘的 dp 题。
\(f[i][0]\)表示到\(i\)为止前缀全部都选的得分。
\(f[i][1]\)表示\(f[i][0]\)的前缀最大值。
\(f[i][2]\)表示到\(i\)且已经舍弃一个区间的最大值。
对于\(f[i][2]\)有两种转移,一种是从之前已经\(f[i-1][2]\)转移,这种是直接接在之前已经舍弃过区间的情况。还有一种是从\(f[i-1][1]\)转移,也就是当前就要舍弃区间的情况。
有一种情况是一个区间都不用舍弃的情况,此时随便舍弃一个也就是答案为\(n-1\)。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
void solve() {
int n;
cin >> n;
vi cnt(3);
for(int i = 1, x, p; i <= n; i++) {
cin >> x;
if(x > cnt[0]) cnt[0] ++;
else if(x < cnt[0]) cnt[0] --;
if(x > cnt[2]) cnt[2] ++;
else if(x < cnt[2]) cnt[2] --;
p = cnt[1];
if(x > p) p ++;
else if(x < p) p --;
cnt[2] = max(cnt[2], p);
cnt[1] = max(cnt[1], cnt[0]);
}
cout << min(n - 1, ranges::max(cnt)) << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while(T --)
solve();
return 0;
}
D. Cool Graph
只要一个点\(x\)的度数大于二,我们任选两个与\(x\)相邻的点\(y,z\)。执行一次操作,至少会使得边的的数量减\(1\),因此这个操作至多执行\(m\)次。
这个操作执行完后,会有两种情况。如果没有边了,则符合条件直接结束。
否则剩下的情况一定是一些孤立的边和孤立的点。我们可以任意选择一条边\((a,b)\),然后对于剩下的孤立边\((x,y)\),我们对\((a,x,y)\)进行一次操作,则会把边\((x,y)\)断掉,并插到\(a\)上,最终会形成一个以\(a\)为中心的菊花图。对于孤立的点\(x\),我们可以执行一次操作\((a,b,x)\),这样就会把\(x\)插到\(a,b\)中间,如果还有孤立点,就插到\(a,x\)中间。最终图就会变成以\(a\)为中心的菊花图插着一条链。
总体操作次数上限是\((m - 1) + (n -1)\)
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using vi = vector<int>;
using pii = pair<int, int>;
void solve() {
int n, m;
cin >> n >> m;
vector<int> f(n + 1, -1);
vector<array<int, 3>> res;
auto add = [&](auto &&self, int x, int y) -> void {
if (f[x] == y and f[y] == x) {
f[x] = f[y] = -1;
} else if (f[x] != -1) {
int z = f[x];
res.push_back({x, y, z});
f[x] = f[z] = -1;
self(self, y, z);
} else if (f[y] != -1) {
int z = f[y];
res.push_back({x, y, z});
f[y] = f[z] = -1;
self(self, x, z);
} else {
f[x] = y, f[y] = x;
}
return;
};
for (int x, y; m; m--) {
cin >> x >> y;
add(add, x, y);
}
vector<pii> edge;
for (int i = 1; i <= n; i++) {
if (f[i] == -1) continue;
if (f[i] < i) continue;
edge.emplace_back(i, f[i]);
}
if (not edge.empty()) {
auto [a, b] = edge[0];
for (int i = 1; i < edge.size(); i++) {
auto [x, y] = edge[i];
res.push_back({a, x, y});
}
for (int i = 1; i <= n; i++) {
if (f[i] != -1) continue;
res.push_back({a, b, i});
b = i;
}
}
cout << res.size() << "\n";
for(auto [x, y, z] : res)
cout << x << " " << y << " " << z << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
E. Common Generator
我们首先求出每个数\(a_i\)最小质因子\(b_i\)。
如果每一个数的最小质因子都不是本身,换句话说每一个数都不是质数,则每个数的次小质因子一定是大于等于\(2\)。也就是说对于每个数都存在\(2b_i \le a_i\)。并且
这样的路径一定存在,所以\(2\)一定是答案。
剩下的情况就是存在一些数是质数。
首先我们要知道,任何一个质数一定不能按照题目的方法被构造。我们可以反证法,如果一个数\(a\)加上自己的因子\(b\)等于一个质数\(p\),则一定有
则\(p\)会有\((k+1)\)和\(b\)这两个因子,不符合质数定义。
因此如果存在两种质数则一定无解。
那么,我们就必须要选择唯一出现的质数\(p\)作为答案,剩下就是我们要判断这个答案是否合法。
-
如果这个数是\(p\)的倍数,一定可以被构造
-
如果这个数小于\(2p\),一定不能被构造,无解。
-
如果这个数是偶数,则一定可以被构造,令\(a_i = 2k\),并且\(a_i \ge 2p\),则一定存在如下构造方法
\[p \rightarrow 2p \rightarrow 2(p+1) \rightarrow 2(p+2) \rightarrow 2k = a_i \] -
如果这个数减最小质因子大于等于\(2p\),则一定可以被构造。首先这个数奇数,且最小质因子一定是奇数,则这个数减最小质因子一定是这个偶数,并且是这个数一个因数,记为\(x\)。因为满足了\(x\ge 2p\),则一定可以用 3 的方法构造出\(x\),再构造出\(a_i\)。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
const int N = 4e5;
vi minp;
void init() {
minp = vi(N + 1, inf);
for (int i = 2; i <= N; i++) {
if (minp[i] != inf) continue;
for (int j = i; j <= N; j += i)
minp[j] = min(minp[j], i);
}
return;
}
void solve() {
int n;
cin >> n;
vi a(n);
for (int i = 0; i < n; i++) cin >> a[i];
int must = -1;
for (int i = 0; i < n; i++) {
if (a[i] != minp[a[i]]) continue;
if (must == -1) must = a[i];
else if (must != a[i]) {
cout << "-1\n";
return;
}
}
if (must == -1) {
cout << "2\n";
return;
}
for (int i = 0; i < n; i++) {
if (a[i] % must == 0) continue;
if (a[i] < must * 2) {
cout << "-1\n";
return;
}
if (a[i] % 2 == 0) continue;
if (a[i] - minp[a[i]] < must * 2) {
cout << "-1\n";
return;
}
}
cout << must << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
init();
int T;
cin >> T;
while (T--)
solve();
return 0;
}