A. Puzzle From the
题意:
将 (a) 和 (b) 两个数(长度相同)相加得到 (c),对于 (c) 中的如果某一段区间上的数字相同就省略只写一个(例如:(112200) 省略为:(120))就得到了 (d)。在已知 (b) 的情况下计算一个 (a) 要求使 (d) 最大。
思路:
显然对于一个数越长就越大,那么 (c) 的相邻位一定是不同的。第一位一定是 (2)。
代码:
int main() {
int t; cin >> t;
while (t --) {
int n;
cin >> n;
string a = "", b;
cin >> b;
int len_b = b.size();
a += '1';
for (int i = 1; i < len_b; i ++) {
if (b[i] + '1' == a[i - 1] + b[i - 1]) a += '0';
else a += '1';
}
cout << a << endl;
}
return 0;
}
B. Different Divisors
题意:
对于 (a) 的所有因数之差大于等于 (d)。求最小的满足条件的 (a)。((a) 至少有四个因数)
思路:
对于一个数来说至少有两个因数那就是 (1) 和他本身。
那么我们从 (1) 开始,找一个 (ge d+1) 的素数 (x)。在找一个 (ge x+d) 的素数 (y)。那么 (x imes y) 就是答案。
之所以找素数是因为如果我们找的是合数,那么对于这个合数还有其他因子,那么就无法判断。
(x imes y) 根据质因数分解为:(x^1 imes y^1)。那么就只有这四个因数,显然 (a - y ge d) 的。所以合理。
我们可以先预处理出素数,然后二分即可。
代码:
const int N = 1e7;
vector<int> primes;
int is_prime[N];
void init() {
memset(is_prime, 0, sizeof is_prime);
for (int i = 2; i < N; i ++) {
if (is_prime[i] == 0) primes.pb(i);
int len_primes = primes.size();
for (int j = 0; j < len_primes && primes[j] * i < N; j ++) {
is_prime[i * primes[j]] = 1;
if (i % primes[j] == 0) break;
}
}
}
int main() {
init();
int len_primes = primes.size();
int t; cin >> t;
while (t --) {
int d; cin >> d;
int a = lower_bound(all(primes), d + 1) - primes.begin();
int b = lower_bound(all(primes), primes[a] + d) - primes.begin();
ll ans = 1ll * primes[a] * primes[b];
cout << ans << endl;
}
return 0;
}
C. Array Destruction
题意:
给定一个数组 (a)。初始选定一个 (x),在数组中找两个数 (a, b) 满足 (a + b = x),那么 (a,b) 就从数组中删去,并且 (x = max(a, b)),继续操作下去,问是否可以把数组中的数删光?如果可以就输出初始的 (x) 和每次删除的 (a, b)。
思路:
显然我们可以知道对于当前的操作下选取的 (a, b) 中的较大值一定是当前数组中的最大值,只有这样才有可能删完。那么根据当前的 (x),又从数组中选出当前的最大值,那么另一个数就可以求出来。只有第一次操作的时候,由于 (x) 的未知,所以无法确定另一个数。
由于数组的长度 (le 2 imes 10^3)。那么我们可以暴力枚举第一次选取的另一个数。复杂度为 (O(n^2))。
代码:
const int N = 2e3 + 10;
map<int, int> mp;
int a[N];
vector<PII> ans;
int main() {
int t; cin >> t;
while (t --) {
mp.clear();
int n; cin >> n;
int flag = 0;
int m = n << 1;
for (int i = 1; i <= m; i ++) {
cin >> a[i];
mp[a[i]] ++;
}
sort(a + 1, a + m + 1);
mp[a[m]] --;
for (int i = 1; i < m; i ++) {
ans.clear();
ans.pb({a[m], a[i]});
int j = m - 1;
map<int, int> tmp_map = mp;
tmp_map[a[i]] --;
int pre = a[m];
while (j > 0) {
if (tmp_map[a[j]]) {
if (pre - a[j] == a[j] && tmp_map[a[j]] >= 2) {
tmp_map[a[j]] -= 2;
ans.pb({a[j], a[j]});
pre = a[j];
} else if (pre - a[j] != a[j] && tmp_map[pre - a[j]]) {
tmp_map[a[j]] --;
tmp_map[pre - a[j]] --;
ans.pb({a[j], pre - a[j]});
pre = a[j];
} else {
break;
}
}
j --;
}
if (ans.size() == n) {
flag = 1;
break;
}
}
if (flag) {
puts("YES");
cout << ans[0].x + ans[0].y << endl;
for (int i = 0; i < n; i ++) cout << ans[i].x << " " << ans[i].y << endl;
} else {
puts("NO");
}
}
return 0;
}
D. Cleaning
题意:
有 (n) 堆石头,每次你可以让相邻位上的石头数 (-1)(前提是有石头)。你有一次特殊操作是交换两堆相邻石头。问最终能否把石头的数量都变为 (0) ?
思路:
根据题意可知,第 (x) 堆石头只能由 (x+1) 堆来清除,直至第 (n) 堆清楚完第 (n-1) 堆后刚好为 (0)。
那么 (O(n^2)) 的做法就是枚举交换的位置,然后判断是否可行即可。
此时我们可以发现在枚举交换位置后的判断有重复的计算,所以我们可以预处理出 (l) 和 (r) 数组,(l[i]) 代表从左往右消除到位置 (i) 上剩余的石头数,(r[i]) 代表从右往左消除到位置 (i) 上的石头数量,如果 (l[i] = r[i + 1]) 就说明不用交换就可以消除完,那么我们枚举交换的位置 (i, i + 1),只需要判断四堆石头即:(l[i - 1], a[i], a[i + 1], r[i + 2]) 是否可以消除完即可。
注:当 (l[i] < 0),那么之后的都是不合法的了。
代码:
const int N = 2e5 + 10;
int a[N];
ll l[N], r[N];
int main() {
int t; cin >> t;
while (t --) {
int n; cin >> n;
memset(l, 0, sizeof l);
memset(r, 0, sizeof r);
int l_flag = n + 1, r_flag = -1;
// LNF 为无穷大
for (int i = 1; i <= n; i ++) {
cin >> a[i];
if (a[i] >= l[i - 1]) l[i] = a[i] - l[i - 1];
else l[i] = LNF / 2;
}
for (int i = n; i >= 1; i --) {
if (a[i] >= r[i + 1]) r[i] = a[i] - r[i + 1];
else r[i] = LNF;
}
int flag = 0;
// 枚举交换的位置
for (int i = 1; i <= n - 1; i ++) {
if (l[i] == r[i + 1]) {
flag = 1;
break;
}
// 交换 i 和 i + 1
if (a[i + 1] >= l[i - 1] && a[i] >= r[i + 2] && a[i + 1] - l[i - 1] == a[i] - r[i + 2]) {
flag = 1;
break;
}
}
if (flag == 1) puts("YES");
else puts("NO");
}
return 0;
}