Codeforces Round 835 (Div. 4)D~G
Codeforces Round 827 (Div. 4)C~G
C. Stripes(思维)
题意
给8 * 8的网格,盖B的话会竖着盖,盖R的话会横着盖。问B还是R最后盖
思路
最后盖的那个肯定可以保留完整的一行/一列(有连续的8格)
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
void solve() {
// 看哪一个字母可以连续8个
int cnt = 0;
vector<vector<char>>mp(10, vector<char>(10));
for (int i = 1; i <= 8; i++) {
for (int j = 1; j <= 8; j++) {
cin >> mp[i][j];
}
}
for (int i = 1; i <= 8; i++) {
cnt = 0;
for (int j = 1; j <= 8; j++) {
if (mp[i][j] != 'R')break;
cnt++;
}
if (cnt == 8) {
cout << "R" << endl;
return;
}
}
for (int i = 1; i <= 8; i++) {
cnt = 0;
for (int j = 1; j <= 8; j++) {
if (mp[j][i] != 'B')break;
cnt++;
}
if (cnt == 8) {
cout << "B" << endl;
return;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _; _ = 1;
cin >> _;
while (_--) solve();
for (auto x : ans)cout << x << endl;
return 0;
}
D. Coprime(gcd,枚举)
题意
给一个数组,从里面取两个下标 i , j(可以相同),使得ai和aj的最大公约数是1。问最大的i+j
思路
记录所有出现过的数字,并且保存每一个数字最后出现的下标
数组从后往前枚举数字ai,然后枚举和ai最大公约数为1的数字,看有没有出现在数组里面,尝试更新i+j的最大值,(数据量不大,所以才想到直接枚举的)
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
void solve() {
// 记录每个数字的最后出现位置
// 从后枚举,看看另一个数字有没有出现,同时尝试更新答案
int n; cin >> n;
vector<int>a(n + 1), num, vis(1005), has(1005);
map<int, int>mp;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (!mp.count(a[i])) {
num.push_back(a[i]);
has[a[i]] = 1;
}
mp[a[i]] = i;
}
int res = -1;
for (int i = n; i >= 1; i--) {
if (vis[a[i]])continue;
vis[a[i]] = 1;
for (int x = a[i]; x >= 1; x--) {
if (has[x] && gcd(a[i], x) == 1) {
res = max(res, mp[x] + i);
}
}
}
ans.push_back(res);
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _; _ = 1;
cin >> _;
while (_--) solve();
for (auto x : ans)cout << x << endl;
return 0;
}
E. Scuza(前缀和,二分,思维)
题意
会先给一个台阶高度的数组k,ki表示第i个台阶高ki,然后告诉一个腿长aj ?没办法跨过高于aj的台阶,问最多累计跨过多少级台阶
思路
假设我们在kj这个台阶,前面的台阶高度为ki,如果前面的台阶比我们现在所在的台阶高的话,那自然是到不了我们这里的,也累加不了我们的台阶高度。于是我们就可以把当前的台阶高度kj假设为前面最高的台阶(但是实际的高度不变)。于是台阶高度就被我们弄成了不严格递增。也就是假设我们可以跨过ki这个台阶,那么直到下一个比ki高的台阶路上的所有台阶,我们都可以累加。
用sum数组记录到第i个台阶,实际累加多少台阶
而数组k就在输入的过程中,如果当前台阶比前面最高的台阶低,那就改为最高的台阶的值
也就是**a[i] = max(a[i],a[i-1]) **
到了询问阶段,给一个高度h的话,说明能跨过的台阶高度不超过h。
因为我们的数组k的单调的,所以可以二分查找到最后一个小于等于h的位置,答案就是这个位置的sum
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
void solve() {
int n, q; cin >> n >> q;
vector<int>a(n + 1);
vector<long long>sumA(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
sumA[i] = sumA[i - 1] + a[i];
a[i] = max(a[i - 1], a[i]);
}
int h;
while (q--) {
cin >> h;
int l = 0, r = n;
int res = 0, mid;
while (l <= r) {
mid = l + r >> 1;
if (a[mid] <= h) {
res = mid;
l = mid + 1;
}
else r = mid - 1;
}
cout << sumA[res] << " ";
}
cout << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _; _ = 1;
cin >> _;
while (_--) solve();
for (auto x : ans)cout << x << endl;
return 0;
}
F. Smaller(字符串,思维)
题意
s和t初始都是"a",每次会给s或者t加上一些字符串,然后每次都会询问有没有可能通过任意的排序使得字典序s小于t
思路
这题关键是要抓住s和t初始都是"a"。
-
如果t加入了除了a以外的字母,
例如b、c、d……那就可以直接把这些字符放到t的第一位,把a放到s的第一位,变成了
s:aXXX
t:bXXX
字典序s < t
-
如果t没有加入除了a以外的字母,t全是a
-
s也同样只有字母a
-
t中a的数量 = s中a的数量
s:aaa
t:aaa
-
t中a的数量 < s中a的数量
s:aaaa
t:aaa
-
t中a的数量 > s中a的数量
s:aaa
t:aaaa
只有第三种情况下,字典序s < t
否则字典序s > t
-
-
s中有其他字母
-
t中a的数量 = s中a的数量
s:aaaX
t:aaa
-
t中a的数量 > s中a的数量
s:aaaX
t:aaaa
-
t中a的数量 < s中a的数量
s:aaaaX
t:aaa
无论如何,字典序s > t
-
-
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
void solve() {
bool sa = true, ta = true;
long long lens = 1, lent = 1;
int q; cin >> q;
while (q--) {
int op, k; cin >> op >> k;
string x; cin >> x;
if (op == 1) {
int temp = 0;
for (char& ch : x) {
if (ch == 'a')temp++;
else sa = false;
}
lens += x.length() * k;
}
if (op == 2) {
int temp = 0;
for (char& ch : x) {
if (ch == 'a')temp++;
else ta = false;
}
lent += x.length() * k;
}
// t含有除了a以外的
if (!ta) {
cout << "YES" << endl;
continue;
}
// t全为a
// s全为a
if (sa) {
if (lent <= lens) {
cout << "NO" << endl;
}
else cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _; _ = 1;
cin >> _;
while (_--) solve();
for (auto x : ans)cout << x << endl;
return 0;
}
G. Orray(位运算)
题意
给了一个数组a,要求前缀或数组b,怎么才能使数组b从第一个数开始就尽可能大(字典序大)
思路
首先想数组b的第一个数字,前缀或的第一个数肯定就只能是它自己。所以数组b的第一个数肯定是最大的那个数字。那后面的数字怎么放?肯定是在所有数字里面挑一个数字或上第一个数字结果最大。
那么结果就出来了,每次选的数字,都要尽可能能和前面已经或出来的结果curMax,或出来得到更大的curMax。
并且呢,由于是或运算,curMax最多最多只能达到什么状态?32位都是1的状态,这样后面不论或甚么,都只能得到32位1
所以,挑数字这个操作,最多最多只需要32次,挑完了32次,由于我们每次操作都是尽可能让前缀或尽可能大的(尽量让这个前缀或能再多一位1),如果操作完32次还是没有到32位1的结果。那其实后面想再变大也不行(如果后面继续挑数字能让它变大,那么在前面32次,早就操作过了)
于是只用管前面32个数字排好就行,后面的数字无所谓,于是我们就不会超时(不是O(n2))
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
void solve() {
int n; cin >> n;
int curMax = 0;
vector<int>a(n), vis(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 每次取一个数字,尽量让curMax|a[i]大
// 最多取32次
vector<int>tar;
for (int i = 0; i < min(32, n); i++) {
int id = -1;
int newMax = curMax;
for (int j = 0; j < n; j++) {
if (vis[j])continue;
if ((curMax | a[j]) > newMax) {
newMax = curMax | a[j];
id = j;
}
}
if (id == -1) {
break;
}
curMax = newMax;
vis[id] = 1;
tar.push_back(a[id]);
}
for (auto x : tar) {
cout << x << " ";
}
for (int i = 0; i < n; i++) {
if (vis[i])continue;
cout << a[i] << " ";
}
cout << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _; _ = 1;
cin >> _;
while (_--) solve();
for (auto x : ans)cout << x << endl;
return 0;
}

浙公网安备 33010602011771号