A. Chess For Three
模拟。
首先可以发现每一次对局三人的得分总和加 \(2\),所以若干次对局后得分总和也一定是 \(2\) 的倍数,然后为了使和棋数量尽可能多,一直让得分最高的两人和棋且得分数各减 \(1\) 直到无法做出和棋为止。
#include <bits/stdc++.h>
using namespace std;
#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()
using i64 = long long;
using i128 = __int128;
const int INF = 0x3f3f3f3f;
void solve() {
vector<int> p(3);
cin >> p[0] >> p[1] >> p[2];
int sum = accumulate(all(p), 0);
if (sum % 2) {
cout << -1 << '\n';
} else {
int ans = 0;
sort(all(p));
while (p[2] != 0 && p[1] != 0) {
p[1]--, p[2]--;
ans++;
sort(all(p));
}
cout << ans << '\n';
}
}
void prework() {
}
int main() {
cctie;
prework();
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
B. Cat, Fox and the Lonely Array
二分、前缀和。
考虑二分 \(k\),判断数组 \(a\) 是否满足 \(k-lonely\),可通过预处理二进制下 \(n\) 个数每一位的前缀和,然后判断时对每两个相邻 \(k\) 子数组判断相等即可,判断相等看每一位的布尔值是否都相同。
证明可二分性:如果数组 \(a\) 为 \(k-lonely\),则有 \((a_i | \ldots | a_{i+k-1}) = (a_{i+1} | \ldots | a_{i+k}) = (a_{j} | \ldots | a_{j+k-1} )= (a_{j+1} | \ldots | a_{j+k})\),而 \((a_i | \ldots | a_{i+k}) = (a_i | \ldots | a_{i+k-1}) | (a_{i+1} | \ldots | a_{i+k}),\ (a_j | \ldots | a_{j+k}) = (a_{j} | \ldots | a_{j+k-1} )|(a_{j+1} | \ldots | a_{j+k})\),则有 \((a_i | \ldots | a_{i+k})=(a_j | \ldots | a_{j+k})\),能推出 \(a\) 为 \(k+1-lonely\)。
时间复杂度: \(O(20nlogn)\)。
#include <bits/stdc++.h>
using namespace std;
#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()
using i64 = long long;
using i128 = __int128;
const int INF = 0x3f3f3f3f;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
vector<array<int, 20>> s(n + 1);
for (int j = 0; j < 20; j++) {
for (int i = 1; i <= n; i++) {
s[i][j] = s[i - 1][j] + (a[i] >> j & 1);
}
}
auto check = [&](int k)->bool {
for (int j = 0; j < 20; j++) {
for (int i = k + 1; i <= n; i++) {
if (!!(s[i][j] - s[i - k][j]) != !!(s[i - 1][j] - s[i - 1 - k][j])) {
return false;
}
}
}
return true;
};
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
cout << l << '\n';
}
void prework() {
}
int main() {
cctie;
prework();
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
C. Cat, Fox and Double Maximum
贪心。
注意到 \(n\) 位偶数,则显然答案的最大值不超过 \(\frac{n}{2}-1\),而这一值总是可以通过以下构造方式实现的。看 \(p\) 排列中 \(n\) 所处位置的奇偶性(可以避免最小值和最大值相邻的情况),若为奇数,则考虑对所有不位于两端且下标位奇数的位置作为山峰(即极大值)去构造,这些位置对应于 \(q\) 排列中的值应贪心的赋予,最小的赋予最大的,以此类推,这样最有可能在该位置构成山峰,而山峰两侧的值则是再不阻碍山峰的前提下赋予尽可能大的值,最后把未放数的位置用剩余的数补充即可;若为偶数,构造方法类似。
时间复杂度: \(O(nlogn)\) 。
#include <bits/stdc++.h>
using namespace std;
#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()
using i64 = long long;
using i128 = __int128;
const int INF = 0x3f3f3f3f;
void solve() {
int n;
cin >> n;
vector<int> p(n + 1);
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
vector<int> ord(n + 1);
iota(ALL(ord), 1);
sort(ALL(ord), [&](int i, int j){return p[i] < p[j];});
set<int> S;
for (int i = 1; i <= n; i++) {
S.insert(i);
}
bool odd = (ord[n] & 1);
vector<int> q(n + 1);
for (int ii = 1; ii <= n; ii++) {
int i = ord[ii];
if (odd) {
if ((i & 1) && i != 1) {
q[i] = *S.rbegin();
S.erase(--S.end());
}
} else {
if (!(i & 1) && i != n) {
q[i] = *S.rbegin();
S.erase(--S.end());
}
}
}
if (odd) {
for (int i = 2; i <= n; i += 2) {
int x = INF;
if (i != n) {
x = min(x, p[i + 1] + q[i + 1]);
}
if (i != 2) {
x = min(x, p[i - 1] + q[i - 1]);
}
auto it = --S.lower_bound(x - p[i]);
q[i] = *it;
S.erase(it);
}
} else {
for (int i = 3; i <= n; i += 2) {
int x = INF;
if (i != n - 1) {
x = min(x, p[i + 1] + q[i + 1]);
}
if (i != 1) {
x = min(x, p[i - 1] + q[i - 1]);
}
auto it = --S.lower_bound(x - p[i]);
q[i] = *it;
S.erase(it);
}
}
for (int i = 1; i <= n; i++) {
if (!q[i]) {
q[i] = *S.begin();
S.erase(S.begin());
}
}
for (int i = 1; i <= n; i++) {
cout << q[i] << " \n"[i == n];
}
}
void prework() {
}
int main() {
cctie;
prework();
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
D. Cat, Fox and Maximum Array Split
交互、思维。
\(l\) 为 \(1\), \(x\) 从 \(n^2\) 枚举到 \(n\) 去询问,当返回值为 \(n\) 时,则得出了数组中最大值 \(mx\),这样的询问最多进行了 \(n\) 次。接着我们知道,答案 \(m\) 存在的情况下,一定为 \(mx\) 的倍数,因为最大值也一定属于划分后 \(k\) 个子数组的某一个,所以我们只需要枚举 \(m\) 的值并判断是否可行即可,从 \(\frac{n}{k}*mx\) 枚举到 \(mx\),也就是枚举了 \(\frac{n}{k}\) 次,而每次判断也只需要最多 \(k\) 次询问,所以这样的询问也最多进行了 \(n\) 次,所以总次数最多进行了 \(2n\) 次。特别注意在输出答案后也要读取一个值。
时间复杂度: \(O(n)\)。
#include <bits/stdc++.h>
using namespace std;
#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()
using i64 = long long;
using i128 = __int128;
const int INF = 0x3f3f3f3f;
void solve() {
int n, k;
cin >> n >> k;
auto query = [&](int l, int x)->int {
cout << "? " << l << " " << x << endl;
int r;
cin >> r;
return r;
};
int mx = n;
while (query(1, n * mx) != n) {
mx--;
}
auto check = [&](int m)->bool {
int cur = 0;
for (int i = 0; i < k; i++) {
if (cur == n) {
return false;
}
int pos = query(cur + 1, m);
if (pos == n + 1) {
return false;
}
cur = pos;
}
return cur == n;
};
auto output = [&](int m)->void {
cout << "! " << m << endl;
int r;
cin >> r;
};
for (int t = n / k; t >= 1; t--) {
int m = t * mx;
if (check(m)) {
output(m);
goto over;
}
}
output(-1);
over:
}
void prework() {
}
int main() {
cctie;
prework();
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
标签:return,int,945,Codeforces,cin,using,Div,ldots,define
From: https://www.cnblogs.com/xiojoy/p/18199430