update: E题python选手读入可能会出问题,现已对赛时提交返回错误代码重测,由于出题人不会python,对于赛时这个问题无法解决,对此深感抱歉orz。
对于 题由于数据问题导致了极少数人本应返回答案错误却返回答案正确,对此出题人深表抱歉,数据已更新,给大家带来的不便敬请谅解。
A.打靶
判断一下是否 即可,注意判断 。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
long long n, m, X, Y;
cin >> n >> m >> X >> Y;
if (n - m >= Y - X && Y >= X)cout << "Yes\n";
else cout << "No\n";
}
}
B.小蓝的疑惑
第一种解法(常规解法):
前置知识:对于任意两个正整数 都有 。
设 ,对 进行因数分解,可以找出所有的 使得 。
对所有的 判断是否满足 且 即可。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
long long x, y;
cin >> x >> y;
long long res = x * y;
bool f = 0;
for (long long i = 1; i * i <= res; i++) {
if (res % i == 0) {
long long j = res / i;
if (gcd(i, j) == x && lcm(i, j) == y) {
cout << i << ' ' << j << '\n';
f = 1;
break;
}
}
}
if (!f)cout << -1 << '\n';
}
}
第二种解法:
前置知识:对于任意两个正整数 都有 。
由于上述条件在,只需要判断一下给定的 是否符合 的条件,若满足直接输出 即为答案,否则输出 。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
long long x, y;
cin >> x >> y;
if (y % x == 0)cout << x << ' ' << y << '\n';
else cout << -1 << '\n';
}
}
以上两种解法复杂度均能通过。
C.𝑘级序列
假设 ,对于 ,设 ,判断是否 即可,如果满足则令 ,否则输出 。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long a[N];
long long b[N];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
b[0] = -0x3f3f3f3f3f3f3f3f;
bool f = 1;
for (int i = 1; i <= n; i++) {
cin >> a[i];
long long c = a[i] - k, d = a[i] + k;
if (d >= b[i - 1])b[i] = max(b[i - 1], c);
else f = 0;
}
if (!f)cout << "No\n";
else cout << "Yes\n";
}
}
D.Reverse
可以发现翻转的区间有一部分贡献的答案和翻转前贡献的答案一样。
已知每次询问独立,对于每次询问,快速查询区间 和 中有多少段连续的 。
设 为第 个字符前面(包括第 个字符)中最近的 出现的位置。
设 为第 个字符后面(包括第 个字符)中最近的 出现的位置。
设 。
若 ,则更新 。
若 ,则更新 。
然后对结果加上区间 中贡献的答案即可。
注意一些代码上的细节。
其中快速查询区间的贡献可以通过前缀和预处理做出。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int pre[N], suf[N];
int dp[N][2];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, q;
cin >> n >> q;
string s;
cin >> s;
s = "0" + s + "0";
int last = 0;
for (int i = 1; i <= n; i++) {
if (s[i] == '0')last = i;
pre[i] = last;
dp[i][0] = dp[i - 1][0];
if (s[i] == '1' && s[i - 1] == '0')dp[i][0]++;
}
dp[n + 1][1] = 0;
last = n + 1;
for (int i = n; i >= 0; i--) {
if (s[i] == '0')last = i;
suf[i] = last;
dp[i][1] = dp[i + 1][1];
if (s[i] == '1' && s[i + 1] == '0')dp[i][1]++;
}
while (q--) {
int l, r;
cin >> l >> r;
int L = l, R = r;
int res = dp[l - 1][0] + dp[r + 1][1];
if (s[l - 1] == '1')R = pre[R];
if (s[r + 1] == '1')L = suf[L];
if (R >= L) {
if (s[L] == '0')res += dp[R][0] - dp[L - 1][0];
else res += dp[L][1] - dp[R + 1][1];
}
if (s[l - 1] == '1' && s[r + 1] == '1' && R < L)res--;
cout << res << '\n';
}
}
E.Dog vs Cat
如果序列长度为 ,那么看操作次数即可判断谁胜谁负。
如果序列长度为 ,可以发现:
如果序列是形如 的形式,那么第一个操作的人为了不败,只能选择第二个元素进行操作,此时若 ,则第一个人输掉了比赛,否则序列将会变成 的形式,此时第二个人必败。
否则第一个人必败。
如果序列长度大于 ,则仅有当 才会分出胜负,否则比赛将一直进行。
证明:若当 时就分出胜负,我们不妨回到最后一步,此时行动的人如果选择序列中的某一个 进行操作,则 更新为 ,且 ,已知区间长度大于 ,因此此时行动的 人并不会被判负,因此当 时就分出胜负是不可能的。
由上述可知,最终 的个数为 ,其余元素全为 时为最终状态,计算总行动次数,判断奇偶即可获得答案。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
if (n == 1) {
if (a[1] == 0 || a[1] & 1)cout << "Dog\n";
else cout << "Cat\n";
} else if (n == 2) {
if (a[1] > a[2])swap(a[1], a[2]);
if (a[2] == 0)cout << "Dog\n";
else {
if (a[2] == a[1] + 1) {
if (a[1] == 0)cout << "Dog\n";
else cout << "Cat\n";
} else cout << "Dog\n";
}
} else {
int i = (n - 1) / 2;
int j = n - i;
long long res = 0;
int cnt0 = 0;
for (int i = 1; i <= n; i++)res += a[i], cnt0 += (a[i] == 0);
if (res == 0 || n - cnt0 <= cnt0)cout << "Dog\n";
else {
res -= j;
if (res & 1)cout << "Cat\n";
else cout << "Dog\n";
}
}
}
}
F.小蓝的构造
对于本题,首先我们可以知道,当且仅当 且 时, ,否 则, ,那么,当题目中给定的 时,我们可以直接构造出 ,并且认为 ,综上所述,我们可以找到一个新的 使得 ,此时则 必有 && 才能使得题目有解 ,于是我们可以知道,两端端点,即此时的 的值是定的。
假设现在 , 表示当前构造出的串中 ~ 。
接着考虑从第 个位置转移到第 , 个位置造成的影响(即从两端往中间构 造),当 并且 之后,将不会对 造成贡献,并且由于串 的两端一定是 ,所以我们不管第 个位置填什么,最终只会使得 的值增加不超过 。于是我们可以考虑从两端向里的过程中维护 ~ ,当我们左端点更新为 时, 则一定无解。
接下来考虑三种情况
、 ,此时第 个位置一定为 ,第 个位置一定为 。
、 , 此时第 个位置要么都是 ,要么都是 。两种情况分别维护即可
、 , 此时第 个位置一定为 ,第 𝑟 个位置一定为 。
综上递归维护即可,最终时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
int f[50];//表示f(t,1)~f(t,n-1)
int a[50];//表示dp(1)~dp(n-1)
int res[50];
int ans[50];//最终输出的答案
int n;
void updatel(int l, int d, int v,
int op) {//更新第[1,d-1]和第 l 位产生的贡献
for (int i = 1; i < d; i++) {
if (res[i] == v)a[l - i] += op;
}
}
void updater(int r, int d, int v,
int op) {//更新第[d+1,n]和第 r 位产生的贡献
for (int i = d + 1; i <= n; i++) {
if (res[i] == v)a[i - r] += op;
}
}
void dfs(int l, int r) {
for (int i = 1; i <= n; i++) {
if (a[i] > f[i])return ;
}
if (l > r) {
for (int i = 1; i < n; i++) {
if (a[i] != f[i])return;
}//不合法
for (int i = 1; i <= n; i++)ans[i] = res[i]; //更新答案
return;
}
if (l == r) { //若L=R,暴力判断此时第L个位置填1还是0即可
res[l] = 1;
updatel(l, l, 0, 1);
dfs(l + 1, r - 1);
updatel(l, l, 0, -1);
res[l] = 0;
updater(l, l, 1, 1);
dfs(l + 1, r - 1);
updater(l, l, 1, -1);
return;
}
if (l == 1) {
res[l] = 0, res[r] = 1;
a[n - l] = 1;
dfs(l + 1, r - 1);
} else {
int len = n - l;
if (a[len] + 2 < f[len])return;
if (a[len] == f[len]) {
res[l] = 1, res[r] = 0;
updatel(l, l, 0, 1);
updater(r, r, 1, 1);
dfs(l + 1, r - 1);
updatel(l, l, 0, -1);
updater(r, r, 1, -1);
} else if (a[len] + 1 == f[len]) {
res[l] = res[r] = 1;
updatel(l, l, 0, 1);
updatel(r, l, 0, 1);
dfs(l + 1, r - 1);
updatel(l, l, 0, -1);
updatel(r, l, 0, -1);
res[l] = res[r] = 0;
updater(l, r, 1, 1);
updater(r, r, 1, 1);
dfs(l + 1, r - 1);
updater(l, r, 1, -1);
updater(r, r, 1, -1);
} else {
res[l] = 0, res[r] = 1;
updatel(r, l, 0, 1);
updater(l, r, 1, 1);
a[r - l]++;
dfs(l + 1, r - 1);
updatel(r, l, 0, -1);
updater(l, r, 1, -1);
a[r - l]--;
}
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i < n; i++)cin >> f[i];
memset(ans, -1, sizeof ans);
int m = n;
while (n > 1 && f[n - 1] == 0)ans[n] = 0, n--;
dfs(1, n);
if (ans[1] != -1) {
for (int i = 1; i <= m; i++)cout << ans[i];
cout << '\n';
} else cout << -1 << '\n';
}
全部评论
(1) 回帖