比赛链接

D

题解

知识点:贪心。

首先,因为第一个人喜欢吃的可能会被后面的人选中,因此直接选最喜欢吃的可能会浪费机会。所以,我们考虑先看后面的人怎么选,就是倒着贪心,我们考虑证明。

假设当前剩下的菜集合为 \(A\) ,还剩下 \(k\) 个人没选,这 \(k\) 个人通过我们的策略选的菜的集合是 \(S_{A,k}\) ,分类讨论:

  1. 若 \(k=1\) ,最优结果与 \(S_{A,1}\) 一致。

  2. 假设 \(k = k'\) 时的最优结果与 \(S_{A,k'}\) 一致。

    当 \(k = k'+1\) 时,设 \(A\) 中除了 \(S_{A,k'}\) 之外倒数第 \(k'+1\) 个人最喜欢的菜是 \(x\) ,对他的选择分类讨论:

    1. 若选择 \(x\) ,则结果与 \(S_{A,k'+1}\) 一致。
    2. 若选择 \(A\) 中除了 \(S_{A,k'}\) 和 \(x\) 之外的菜,则后 \(k'\) 个人结果不变,倒数第 \(k'+1\) 个人亏。
    3. 若选择 \(S_{A,k'}\) 中的菜,那么后 \(k'\) 个人有一个人的选择会改变:
      1. 若改变的那个人选择了 \(x\) ,则结果与 \(S_{A,k'+1}\) 一致。
      2. 若改变的那个人没选择 \(x\) ,则倒数第 \(k'+1\) 个人亏。

    因此 \(k = k'+1\) 时的最优解与 \(S_{A,k'+1}\) 一致,因此贪心策略是对的。

时间复杂度 \(O((n+k)m)\)

空间复杂度 \(O(nm + k)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[2007][2007];
int ans[2007];
bool solve() {
int n, m, k;
cin >> n >> m >> k;
for (int i = 1;i <= n;i++)
for (int j = 1;j <= m;j++)
cin >> a[i][j]; vector<char> vis(m + 1);
for (int i = k;i >= 1;i--) {
int p = i % n;
if (!p) p = n; int res = 0;
for (int j = 1;j <= m;j++) if (!vis[j] && a[p][res] < a[p][j]) res = j;
ans[i] = res;
vis[res] = 1;
} sort(ans + 1, ans + k + 1);
for (int i = 1;i <= k;i++) cout << ans[i] << " \n"[i == k];
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

E

题解

知识点:数学。

显然 \(y^2 \in [x \cdot 10^k,(x+1) \cdot 10^k),k \in[0,18]\) 。注意 \(y^2 \leq 10^{18}\) ,因此我们先保证 \(x \cdot 10^k \leq 10^{18}\) 。

我们枚举 \(k\) ,得到 \(y\) 可能范围的左端点 \(l =\left\lceil \sqrt{x \cdot 10^k} \right\rceil\) ,若 \(l^2 \leq \min(10^{18},(x+1) \cdot 10^k)\) ,那么 \(y = l\) 。

否则,无解。

时间复杂度 \(O(1)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; ll sqrt_fix(ll x, bool mode = false) {
ll v = sqrt(x);
while (v * v < x) ++v;
while (v * v > x) --v;
if (mode && v * v < x) v++;
return v;
} bool solve() {
ll x;
cin >> x;
for (__int128_t i = x, j = x + 1;i <= (ll)1e18;i *= 10, j *= 10) {
ll l = sqrt_fix(i, 1);
if (l * l <= min(j - 1, (__int128_t)1e18)) {
cout << l << '\n';
return true;
}
}
return false;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

F

题解

知识点:博弈论,二分图。

一个至关重要的模型,二分图博弈:

给出一张二分图和起点,轮流进行操作,每次操作只能选择与上一个被选的点相邻的点,且不能选择已经选择过的点,无法选点的人输掉。

关于上面的模型,结论是:若起始点一定在最大匹配上,那么先手赢,否则后手赢。

那和这道题有什么关系呢?

这道题我们把三元组 \((r,g,b)\) 抽象成三维坐标的一个点,那么所有状态构成一个 \(n^3\) 正方体,每个点可以走到相邻的点,那么这道题就变成了一个二分图博弈了,不过图是在三维上建的。接下来分类讨论:

  1. 当 \(n\) 为偶数时,显然每个点都可以匹配到,因此所有点一定在最大匹配上,所以先手必胜。

  2. 当 \(n\) 为奇数时,显然会有一个点在点较多一部中且没被匹配到,我们将证明这个点可以是较多一部中的任意一个点。

    首先,若点 \((r_0,g_0,b_0)\) 在较多一部中,那么 \(r_0+b_0+g_0\) 是一个奇数。若 \(r_0+g_0+b_0\) 是偶数,那么一定可以走一步,就可以与它匹配。

    因此, \(r_0,g_0,b_0\) 中一定有至少一个奇数,我们不妨假设 \(r_0\) 是奇数,显然 \(r \neq r_0\) 时的点都可以匹配到,所以我们可以只考虑 \(r = r_0\) 这一层的情况。

    因为 \(g_0+b_0\) 一定是个偶数,我们发现,总能构造出一种方法使得 \((r_0,g_0,b_0)\) 不在其中。

    综上, \(r+b+g\) 为奇数时,后手必胜,否则先手必胜。

时间复杂度 \(O(1)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int n, r, g, b;
cin >> n >> r >> g >> b;
if ((n & 1) && (r + g + b & 1)) cout << "Bob" << '\n';
else cout << "Alice" << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

G

题解

知识点:manacher。

中心对称字符串和回文串可以理解为是同一个东西,不过匹配方式需要自定义。

这里需要用manacher算法,其中的细节是需要修改的。

接下来是关于本题的最关键的一个性质:若一个字符串能被分为若干个中心对称串,那么它一定能分为若干个任意前缀不是中心对称的中心对称串。

这个结论让我们在从左到右枚举对称中心的途中,对于一个好的前缀,可以往后截断一个最短的前缀中心对称串,而不影响答案。

若最后整个字符串能被截断完了,那么就是好的,否则不好。

证明:

假设一个中心对称串 \(S = AB\) ,其中 \(A\) 是最短的前缀对称串。

我们证明 \(|A| \leq |S|/2\) 。若 \(|A|> |S|/2\) ,那么 \(B\) 的对称串 \(B’\) ,有 \(A = B'C\) ,使得 \(S = B'CB\) 。 那么,其中 \(C\) 一定是中心对称串,而 \(C\) 在 \(A\) 内,因此 \(A\) 的前缀一定有 \(C\) 的对称串也是中心对称的,因此 \(A\) 就不是最短的。

现在对于一个中心对称串 \(S\) 一定可以表达为 \(S = ABA'\) ,其中 \(A,B,A'\) 都是中心对称串,即一个中心对称串若含有更短的前缀中心对称串,那么就可以继续分下去,直到不包含任意前缀中心对称串。

这个结论同样适用于任何具有回文性质的字符串划分,都可以依次截断最短的前缀回文。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; char rev[256]; void init() {
rev['o'] = 'o';
rev['s'] = 's';
rev['x'] = 'x';
rev['z'] = 'z';
rev['b'] = 'q';
rev['q'] = 'b';
rev['d'] = 'p';
rev['p'] = 'd';
rev['n'] = 'u';
rev['u'] = 'n';
rev['|'] = '|';
} bool check(char a, char b) {
return b == rev[a];
} bool manachar(const string &_s) {
string s = "$|";
for (int i = 1;i < _s.size();i++) {
s += _s[i];
s += "|";
}
s += "&";
int n = s.size() - 1;
vector<int> d(n + 1); int start = 1;
int p = 0, r = 0;
for (int i = 1;i <= n - 1;i++) {
if (i <= r) d[i] = min(r - i + 1, d[2 * p - i]);
else d[i] = 0;
while (check(s[i - d[i]], s[i + d[i]])) d[i]++;
if (i + d[i] - 1 > r) {
p = i;
r = i + d[i] - 1;
}
if (i - d[i] + 1 <= start) {
start = i + d[i];
s[start - 1] = '$';
i = start - 1;
}
}
return start == n;
}
bool solve() {
string s;
cin >> s;
s = "?" + s;
cout << (manachar(s) ? "Yes" : "No") << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
init();
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

H

题解

知识点:位运算,前缀和。

考虑快速求出操作区间对初始数字 \(x\) 的影响。

这里一个重要的转换是,操作A对 \(x\) 取反等价于 \(-x-1\) ,因此考虑将答案表达为 \(kx+b,k\in\{-1,1\}\) 的形式。

显然,只有操作A能改变 \(k\) ,因此 \(k\) 即为操作区间 \([l,r]\) 中操作A的个数。

因为操作A能改变 \(b\) 的正负,所以任何操作对 \(b\) 的影响,与之后经历操作A的次数有关,我们分类讨论:

  1. 对于操作B,若其后有偶数个操作A,那么贡献为 \(1\) ,否则为 \(-1\) 。
  2. 对于操作A,若其后有偶数个操作A,那么贡献为 \(-1\) ,否则为 \(1\) 。

我们发现这个过程是能用前缀和维护的。具体来说,我们分别记录 \(k,b\) 在前缀 \([1,i](i = 1,\cdots,n)\) 操作下的结果。

现在我们询问 \([l,r]\) 区间的结果。显然, \(k_{l;r} = k_{l-1} \cdot k_r\) 。对于 \(b\) 我们有 \(b_{l;r} = b_{r} - k_{l;r} \cdot b_{l-1}\) ,因为对于 \([1,r]\) 的操作中,我们需要消除 \([1,l-1]\) 对 \(b\) 的影响,因为这一段操作被之后 \([l,r]\) 的操作A影响过了,因此我们需要对 \(b_{l-1}\) 修正,即 \(b_{l-1} \cdot k_{l;r}\) 。

最终结果为 \(k_{l} \cdot k_{r} \cdot (x - b_{l-1}) + b_r\) 。

当然这个过程还能通过初等矩阵维护。

时间复杂度 \(O(n+q)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int k[200007];
int b[200007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, q;
cin >> n >> q;
string s;
cin >> s;
s = "?" + s;
k[0] = 1;
for (int i = 1;i <= n;i++) {
if (s[i] == 'A') {
k[i] = -k[i - 1];
b[i] = -b[i - 1] - 1;
}
else {
k[i] = k[i - 1];
b[i] = b[i - 1] + 1;
}
} ll ans = 0;
while (q--) {
int l, r;
cin >> l >> r;
l = (ans ^ l) % n + 1;
r = (ans ^ r) % n + 1;
if (l > r) swap(l, r); string t;
cin >> t;
ll x = 0, P = 1LL << t.size();
for (auto ch : t) (x <<= 1) |= ch == '1'; x -= b[l - 1];
x *= k[l - 1];
x *= k[r];
x += b[r];
((x %= P) += P) %= P; ans = x;
for (int i = t.size() - 1;i >= 0;i--) cout << ((x >> i) & 1);
cout << '\n';
}
return 0;
}

I

题解

知识点:构造。

构造形如下方,注意字符顺序要严格:

xoxoxo
oxoxox
oxoxox
xoxoxo
......

当且仅当 \(n = 4k-1,k \in \N^+\) 且 \(m\) 是奇数时,这种构造需要将 ox 反转,变为:

oxoxo
xoxox
xoxox

这是为了保证数量的严格。

可以证明 \(n,m\) 有一个为偶数时,这种构造一定是合法的。

当满足 \(n = 4k-1,k \in \N^+\) 且 \(m\) 是奇数时,发现原来的构造会导致 ox 多一个。

时间复杂度 \(O(nm)\)

空间复杂度 \(O(nm)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int n, m;
cin >> n >> m;
bool f = (n / 2 & 1) && (n & 1) && (m & 1);
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
if (j & 1) cout << ((i / 2 & 1) ^ f ? "o" : "x");
else cout << ((i / 2 & 1) ^ f ? "x" : "o");
}
cout << '\n';
}
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

K

题解

知识点:线性dp。

若没有向右移动,这道dp只需要多记录上一个位置有没有盖子即可。

但这道题可以向右移动,因此再多记录一维上一个位置若原来有盖子,有没有向右移动。

还有其他的状态方案,具体可以看代码。

我用的是方法一,上面讲的是方法二的,我觉得方法二更通用。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[1000007];
bool b[1000007];
ll f[1000007][4]; // 第i个位置,没有/前面/自己/后面拿盖子
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i <= n;i++) cin >> b[i]; for (int i = 1;i <= n;i++) {
f[i][0] = max({ f[i - 1][0],f[i - 1][1],f[i - 1][2],f[i - 1][3] });
if (b[i - 1]) {
f[i][1] = max({ f[i - 1][1], f[i - 2][0],f[i - 2][1],f[i - 2][2] }) + a[i];
}
if (b[i]) {
f[i][2] = max({ f[i - 1][0],f[i - 1][1],f[i - 1][2] }) + a[i];
}
if (b[i + 1]) {
f[i][3] = max({ f[i - 1][0],f[i - 1][1],f[i - 1][2],f[i - 1][3] }) + a[i];
}
}
cout << max({ f[n][0],f[n][1],f[n][2] }) << '\n';
return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long; const int inf = 1e9;
int a[1000007];
bool b[1000007];
ll f[1000007][2][2]; // 前i个位置,有无盖子,有无右移(btw:只有左移操作那只需要有无盖子)
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i <= n;i++) cin >> b[i]; f[0][0][0] = 0;
f[0][0][1] = -inf;
f[0][1][0] = -inf;
f[0][1][1] = -inf;
for (int i = 1;i <= n;i++) {
if (b[i]) {
f[i][0][0] = f[i - 1][0][0] + a[i - 1];
f[i][0][1] = max(f[i - 1][0][0], f[i - 1][1][0]);
f[i][1][0] = max(f[i - 1][0][0], f[i - 1][1][0]) + a[i];
f[i][1][1] = max(f[i - 1][0][1], f[i - 1][1][1]) + a[i];
}
else {
f[i][0][0] = max({ f[i - 1][0][0],f[i - 1][1][0] });
f[i][0][1] = -inf;
f[i][1][0] = max({ f[i - 1][0][1],f[i - 1][1][1] }) + a[i];
f[i][1][1] = -inf;
}
}
cout << max({ f[n][0][0],f[n][1][0] }) << '\n';
return 0;
}

方法三

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[1000007];
bool b[1000007];
ll f[1000007][3]; // 第i个位置,有给前面,没有不管/有自己用,没有从前面拿/一定有且给后面
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i <= n;i++) cin >> b[i]; for (int i = 1;i <= n;i++) {
if (b[i]) {
f[i][0] = f[i - 1][0] + a[i - 1];
f[i][1] = max(f[i - 1][0], f[i - 1][1]) + a[i];
f[i][2] = max({ f[i - 1][0], f[i - 1][1], f[i - 1][2] }) + a[i + 1];
}
else {
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
f[i][1] = f[i - 1][2];
}
}
cout << max(f[n][0], f[n][1]) << '\n';
return 0;
}

2023牛客暑期多校训练营2 DEFGHIK的更多相关文章

  1. 2019牛客暑期多校训练营(第五场)G - subsequeue 1 (一题我真的不会的题)

    layout: post title: 2019牛客暑期多校训练营(第五场)G - subsequeue 1 (一题我真的不会的题) author: "luowentaoaa" c ...

  2. 2021牛客暑期多校训练营3 J 思维

    传送门 J-Counting Triangles_2021牛客暑期多校训练营3 (nowcoder.com) 题目 Goodeat finds an undirected complete graph ...

  3. B-xor_2019牛客暑期多校训练营(第四场)

    题意 给出n个数组(每组数个数不定),m个询问 l, r, x 序号在区间\([l,r]\)的每个数组是否都可以取出任意个数异或出x 题解 判断一个数组能否异或出x,是简单的线性基问题 判断多个线性基 ...

  4. 2019牛客暑期多校训练营(第九场)A:Power of Fibonacci(斐波拉契幂次和)

    题意:求Σfi^m%p. zoj上p是1e9+7,牛客是1e9:  对于这两个,分别有不同的做法. 前者利用公式,公式里面有sqrt(5),我们只需要二次剩余求即可.     后者mod=1e9,5才 ...

  5. 2019牛客暑期多校训练营(第一场)A题【单调栈】(补题)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 题目描述 Two arrays u and v each with m distinct elem ...

  6. 2019牛客暑期多校训练营(第一场) B Integration (数学)

    链接:https://ac.nowcoder.com/acm/contest/881/B 来源:牛客网 Integration 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 5242 ...

  7. 2019牛客暑期多校训练营(第一场) A Equivalent Prefixes ( st 表 + 二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A 来源:牛客网 Equivalent Prefixes 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/ ...

  8. 2019牛客暑期多校训练营(第二场)F.Partition problem

    链接:https://ac.nowcoder.com/acm/contest/882/F来源:牛客网 Given 2N people, you need to assign each of them ...

  9. 2019牛客暑期多校训练营(第八场)E.Explorer

    链接:https://ac.nowcoder.com/acm/contest/888/E来源:牛客网 Gromah and LZR have entered the fifth level. Unli ...

  10. 2019牛客暑期多校训练营(第一场)A Equivalent Prefixes(单调栈/二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 Two arrays u and v each with m distinct elements ...

随机推荐

  1. 28-PWA

    const { resolve } = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin') ...

  2. 关于页面重定向https安全漏洞和服务器SSH加密算法漏洞

    1.HTTP 严格传输安全 nginx配置 add_header Strict-Transport-Security "max-age=63072000; includeSubdomains ...

  3. python 快速替换csv数据集字符串列表中的表情符号为空,asyncio,re,pandas

    传统的字符串列表替换字符串使用遍历非常慢 比如下面这段代码,如果处理几十万或上百万的数据集时,会非常的慢,几小时几天都可能 import re p = re.compile(u'['u'\U0001F ...

  4. live555中ts流详细解析

    live555中ts流详细解析 该文档主要是对live555源码下testProgs中testMPEG2TransportStreamer服务器端的详细分析.主要分析ts流实现的总体调用流程.(重新整 ...

  5. “StackLLaMA”: 用 RLHF 训练 LLaMA 的手把手教程

    如 ChatGPT,GPT-4,Claude语言模型 之强大,因为它们采用了 基于人类反馈的强化学习 (Reinforcement Learning from Human Feedback, RLHF ...

  6. 2021-04-25:给定一个数组arr,和一个正数M,返回在arr的子数组在长度不超过M的情况下,求最大的累加和。

    福大大 答案2021-04-25: 前缀和+左大右小的双端队列.时间太晚了,所以写得简单. 代码用golang编写.代码如下: package main import ( "containe ...

  7. SQL Server2019 新增字段并设置默认值

    命令: ALTER TABLE 表名 add 列名 数据类型 default 默认值 not null 例如: ALTER TABLE LJEL005H add el_req int default ...

  8. 禁用input自动补全,模拟type=password输入字符显示为星号

    最近遇到一个想禁用浏览器的密码自动补全的功能,翻遍了整个技术论坛大多使用用auto-complete="new-password"但是本人测试不怎么管用,所有又找到了如下几种方法, ...

  9. 微软Build 2023两大主题:Copilots和插件

    在本周大型微软人工智能 2023 开发者大会的开幕式上,人工智能站到了舞台中央--前台和后台以及介于两者之间的所有舞台. 贯穿会议的两个主要主题是Copilots - 涵盖广泛产品和服务的AI助手 - ...

  10. K8S in Action 读后感(概念简介)

    一.K8S的用武之地 今天,大型单体应用正被逐渐拆分成小的.可独立运行的组件,我们称之为微服务.微服务彼此之间解耦,所以它们可以被独立开发.部署.升级.伸缩.这使得我们可以对每一个微服务实现快速迭代, ...