「IXOI R3」贴吧 82 号
把每个位置需要的翻转次数写成除数前缀异或,按下标递增唯一决定每个操作是否选择。
OJ: luogu
题目 ID: P17414
难度:普及-
标签:数论异或贪心
日期: 2026-09-06 19:06
形式化题目
给定长度为 n 的 01 串。选择一个正整数 x 会翻转所有 x 的倍数位置,求把字符串变成全 1 所需的最少选择次数。
暴力解法
思路
每个操作重复两次会抵消,所以操作
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-09-06 19:06
* update_at: 2026-09-06 19:22
*/
// brute.cpp:小数据暴力解,枚举每个操作是否选择,用来辅助对拍。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
string s;
cin >> n >> s;
int answer = n + 1;
int all = 1 << n;
for (int mask = 0; mask < all; mask++) {
string t = s;
int used = 0;
for (int x = 1; x <= n; x++) {
if (((mask >> (x - 1)) & 1) == 0) {
continue;
}
used++;
for (int j = x; j <= n; j += x) {
t[j - 1] = (t[j - 1] == '0' ? '1' : '0');
}
}
if (t.find('0') == string::npos) {
answer = min(answer, used);
}
}
cout << answer << '\n';
return 0;
}复杂度与瓶颈
每个方案最多模拟
正解
思路
设
按 i=1,2,...,n 处理。操作 i 只会影响 i 的倍数,不会影响更小的位置;因此处理位置 i 时,当前异或值已经由更小的操作唯一确定:
- 若当前值已经等于
t_i,不选i; - 否则必须选
i,并把它加入所有倍数的当前影响。
这同时说明了选择方案的唯一性,也是从指数枚举降到调和级数复杂度的关键。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-09-06 19:06
* update_at: 2026-09-06 19:22
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
string s;
cin >> n >> s;
vector<unsigned char> current(n + 1, 0);
int answer = 0;
// 按下标从小到大决定是否操作 i。操作 i 只影响 i 的倍数,
// 所以处理到 i 时,current[i] 已经只包含更小操作的影响。
for (int i = 1; i <= n; i++) {
int need = (s[i - 1] == '0');
if (current[i] != need) {
answer++;
for (int j = i; j <= n; j += i) {
current[j] ^= 1;
}
}
}
cout << answer << '\n';
return 0;
}复杂度
操作
总结
把“翻转”改成 GF(2) 上的异或后,除数关系可以从小到大逐点消元。每一步不是试探,而是被当前位置的目标奇偶性唯一确定。