VP Refact.ai Match 1 (Codeforces Round 985)
A. Set
题意:给你[l, r]中的所有数和一个k,每次操作选一个数,这个数在剩下的数里有至少k个倍数,然后删掉这个数。求最大操作次数。
我们删除一个数不会影响后面的数能不能删,所以应该从小到大删,发现如果r / x大于等于k,那么x可以被删,那么最后一个满足的x就是r / k,看和l之间有多少数就行。
点击查看代码
void solve() {
int l, r, k;
std::cin >> l >> r >> k;
std::cout << std::max(0, r / k - l + 1) << "\n";
}
B. Replacement
题意:一个长度为n的01字符串s和一个长度为n-1的01字符串t,你要进行n-1次操作,每次选择一个位置i使得\(s_i\) \(\neq\)\(s_{i+1}\), 然后用\(t_i\)替换掉这两个字符,问能不能完成这n-1次操作。
正解是每次操作显然要让1的数量-1或者让0的数量-1,每次判断每次1和0的数量够不够就行了。
我是直接模拟的,分别找左边连续相同的和右边连续相同的,假设左边是连续的1,我要操作一个0,那么我们把左边的左边界右移,表示用掉一个1,如果操作数和左边的数相同,就看右边。如果两边都和操作数相同,就随便操作一边,这种情况这一边的左右边界都要变。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s, t;
std::cin >> s >> t;
int l1 = 0, r1 = 0;
int l2 = n - 1, r2 = n - 1;
for (auto & c : t) {
while (r1 < n && s[r1] == s[l1]) {
++ r1;
}
while (l2 >= 0 && s[l2] == s[r2]) {
-- l2;
}
if (r1 > r2 || l2 < l1) {
std::cout << "NO\n";
return;
}
if (s[l1] != c) {
++ l1;
} else if (s[r2] != c) {
-- r2;
} else {
s[r1] = s[l1];
++ r1, ++ l1;
}
}
std::cout << "YES\n";
}
C. New Rating
题意:给你n个数,你要选一段连续区间,然后你的初始分为0,一次操作,如果这个位置在你选的区间里,就跳过。否则如果\(a_i\)>x, x += 1, 如果\(a_i\)<x,x -= 1,相等不变。问可以操作出来的x最大值。
对于某个位置i,我们可以选前面L到i-1的区间,这意味着1到L-1是要操作的,那么我们记录下\(max_i\)表示按顺序操作到1~i-2位置的最大x值,因为他是加1加1上去的,那么意味着我们可以选[1, \(max_i\)]作为第i个位置开始的数,我们肯定选最大的,对每个位置模拟一遍是取前面某个max操作过来好,还是直接选当前max好就行。具体算法为:我们开始选x = \(max_1\),拿它和\(a_1\)比较,然后到了2这个位置,我们看是x大还是\(max_2\)大,然后这样循环下去,每次选最大的。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int x = 0, ans = 0;
std::vector<int> max(n);
for (int i = 0; i + 1 < n; ++ i) {
max[i + 1] = ans;
if (a[i] > x) {
++ x;
} else if (a[i] < x) {
-- x;
}
ans = std::max(ans, x);
}
x = 0;
for (int i = 1; i < n; ++ i) {
x = std::max(x, max[i]);
if (x < a[i]) {
++ x;
} else if (x > a[i]) {
-- x;
}
}
ans = std::max(ans, x);
std::cout << ans << "\n";
}
D. Cool Graph
题意:给你一个图,你每次可以选三个点,如果这三个点中某两个点有边,就删掉这条边,否则加上这条边。要删完所有边或者变成一棵树。操作次数不能超过2*max(n, m)。
变成一个空图有点难,我们先往变成树的方向想。如果一个点有两条边,那么我们选择这个点和两条边连着的点,是不是至少会减一条边,那么我们最大m次操作就没有这样的点了。
这时图变成了一些没边的点和一些两个点相连的情况,我们随便找一个两个点相连,设这两个点为root和x,那么我们每次找一个和这两个点不相通的点进行操作,这个点就加入这两个点的连通块,并且这个连通块一直是一棵树,然后把x更新为这个点就行。
这样最多操作n次,n+m<=2*max(n,m)。满足条件。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::set<int> > adj(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].insert(v);
adj[v].insert(u);
}
if (m == 0) {
std::cout << 0 << "\n";
return;
}
std::vector<std::array<int, 3> > ans;
auto work = [&](int a, int b) -> void {
if (adj[a].count(b)) {
adj[a].erase(b);
adj[b].erase(a);
} else {
adj[a].insert(b);
adj[b].insert(a);
}
};
for (int i = 0; i < n; ++ i) {
while (adj[i].size() >= 2) {
int a = i, b = *adj[i].begin(), c = *next(adj[i].begin());
work(a, b); work(a, c); work(b, c);
ans.push_back({a, b, c});
}
}
std::vector<int> fa(n);
std::iota(fa.begin(), fa.end(), 0);
std::function<int(int)> find = [&](int x) -> int {
return x == fa[x] ? x : fa[x] = find(fa[x]);
};
int root = 0;
for (int i = 0; i < n; ++ i) {
if (adj[i].size() == 1) {
fa[find(*adj[i].begin())] = find(i);
root = i;
}
}
if (adj[root].size() == 1) {
int x = *adj[root].begin();
for (int i = 0; i < n; ++ i) {
if (find(i) != find(root)) {
work(root, x); work(root, i); work(i, x);
ans.push_back({root, i, x});
fa[find(i)] = find(root);
x = i;
}
}
}
std::cout << ans.size() << "\n";
for (auto & [a, b, c] : ans) {
std::cout << a + 1 << " " << b + 1 << " " << c + 1 << "\n";
}
}
E. Common Generator
题意:如果d | x, x可以变成x + d(d >= 2)。给你一组数,问有没有一个x可以变成其中任意一个。
- 2可以变成所有合数。
2显然可以变成所有偶数,对于一个是奇数的合数x,它必然有一个比它小的奇数因子y,因为y | x, y | y, 所以y | x - y,因为x - y一定是偶数,所以2可以变成所有为奇数的合数。 - 质数只能由自己变过来。
假设对于某个质数p,可以由前面一个数x加d变过来,那么因为d | x, 所以d | p, 我们知道p只有1和p两个因子,因为1不能选,那么d = p, 则x = 0,这是不合法的。 - 数组里最多只有一个质数
根据结论2,如果由有两个以上质数则只能变一个质数,其他都变不了。 - 如果一个偶数x和一个质数p满足x \(\geqslant\) 2 * p,那么p可以变成x,否则不可以。
因为2 | 2 * p, 所以可以一直加2到x。否则因为p能变的最小的两个数就是p和2 * p,不能变成其他小于2 * p的数,所以如果x 小于 2 * q, 则p不能变成x。 - 如果一个奇数和一个质数p满足x - \(minp_x\) \(\geqslant\) 2 * p(其中\(minp_x\)是x的最小质因子),那么p可以变成x。否则不可以。
因为x - \(minp_x\)是偶数,所以2 * p可以变成x - \(minp_x\),因为\(minp_x\) | x, 所以 \(minp_x\) | x - \(minp_x\), 可以变成x。否则p无法变成除p外的小于2 * p的数,不可能变成x。
所以我们的做法是,先预处理质数和minp, 然后每个样例判断有几个质数,如果没有就直接输出2,有一个就判断这个质数能不能变成每个数,有两个或两个以上就输出-1。
点击查看代码
const int N = 4e5 + 5;
std::vector<int> primes;
int minp[N];
void init(int n) {
for (int i = 2; i <= n; ++ i) {
if (!minp[i]) {
primes.push_back(i);
minp[i] = i;
}
for (auto & p : primes) {
if (p > n / i) {
break;
}
minp[p * i] = p;
if (i % p == 0) {
break;
}
}
}
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::set<int> s;
for (auto & x : a) {
if (minp[x] == x) {
s.insert(x);
}
}
if (s.size() == 0) {
std::cout << 2 << "\n";
} else if (s.size() == 1) {
int p = *s.begin();
for (auto & x : a) {
int y = x % 2 == 0 ? x : x - minp[x];
if (x != p && y < 2 * p) {
std::cout << -1 << "\n";
return;
}
}
std::cout << p << "\n";
} else {
std::cout << -1 << "\n";
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);
init(4e5);
int T = 1; std::cin >> T;
while (T -- ) {
solve();
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析