Codeforces Round 799 (Div. 4)D~H
Codeforces Round 799 (Div. 4)D~H
D. The Clock(模拟)
题意
给初始时间和一个间隔时间,每一次间隔时间都会看一次表,直到出现重复
思路
模拟,字符串转数字,数字转字符串。如果HH大于24,对24取模。如果MM大于60,那就HH+=MM/60,然后MM对60取模。每次判断是不是回文串就行
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
bool check(string s) {
for (int i = 0; i < s.length() / 2; i++) {
if (s[i] != s[s.length() - i - 1])return false;
}
return true;
}
void solve() {
string s; cin >> s;
int x; cin >> x;
int nh = x / 60;
int nm = x % 60;
int h, m;
string s1 = s.substr(0, 2);
string s2 = s.substr(3, 2);
h = stoi(s1); m = stoi(s2);
s1 = to_string(h);
s2 = to_string(m);
if (s1.length() == 1)s1 = "0" + s1;
if (s2.length() == 1)s2 = "0" + s2;
string t = s1 + s2;
string S;
int cnt = 0;
do {
h += nh;
m += nm;
h += m / 60;
m %= 60;
if (h >= 24) h %=24;
s1 = to_string(h);
s2 = to_string(m);
if (s1.length() == 1)s1 = "0" + s1;
if (s2.length() == 1)s2 = "0" + s2;
S = s1 + s2;
if (check(S))cnt++;
} while (t != S);
ans.push_back(cnt);
}
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. Binary Deque(双指针、滑动窗口)
题意
给一个只包含1和0的数组,要从开头或者结尾删掉一些数字,使得数组总和为s
思路
双指针滑动窗口,维护区间,使得区间里面的和永远是s
l起始数组开头的下标,r初始是使得l~r的和为s的位置
然后枚举r,枚举r的过程中,如果区间和sum不合法,移动左指针l,每次尝试更新答案,应该是O(n)
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
void solve() {
// 双指针滑动窗口
int n, s; cin >> n >> s;
vector<int>a(n + 1),sumA(n+1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
sumA[i] = sumA[i - 1] + a[i];
}
if (sumA[n] < s) {
ans.push_back(-1);
return;
}
int l = 1, r = 1;
while (sumA[r] - sumA[l - 1] < s) {
r++;
}
int cost = n - r + l - 1;
for (; r <= n; r++) {
while (sumA[r] - sumA[l - 1] > s) {
l++;
}
cost = min(cost, n - r + l - 1);
}
ans.push_back(cost);
}
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. 3SUM(枚举)
题意
给一个数组,找三个不同的位置,使得ai+aj+ak以数字3结尾
思路
因为要最后一位数字是3,那取的那三个数字也只要看最后一位就行。记录数组中,每个数字(0~9)作为最后一位出现的次数。现在要从个位数里面找三个数字加起来是以3结尾的,三个个位数加起来以三结尾的结果只能是3、13、23。从0~9枚举第一个数字i,然后从0~9枚举第二个数字j,最后k就不枚举了,直接算。假设结果为3,算一个k出来,假设结果为13,算一个k出来,假设结果为23,也算一个k出来。判断k是否属于[0,9],再在原数字里面看以k结尾的数字还够不够。
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<string>ans;
void solve() {
int n; cin >> n;
int cnt[10] = { 0 };
for (int i = 0, a; i < n; i++) {
cin >> a;
cnt[a % 10]++;
}
for (int i = 0; i <= 9; i++) {
if (!cnt[i])continue;
cnt[i]--;
for (int j = 0; j <= 9; j++) {
if (!cnt[j])continue;
cnt[j]--;
int k = 23 - (i + j);
if (0 <= k && k <= 9 && cnt[k]) {
ans.push_back("YES");
return;
}
k = 13 - (i + j);
if (0 <= k && k <= 9 && cnt[k]) {
ans.push_back("YES");
return;
}
k = 3 - (i + j);
if (0 <= k && k <= 9 && cnt[k]) {
ans.push_back("YES");
return;
}
cnt[j]++;
}
cnt[i]++;
}
ans.push_back("NO");
}
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. 2^Sort(数学,前缀和)
题意
给一个数组,求一个满足一些条件的,长度为k+1的子数组的数量
这个子数组会把原数组里面的数,都乘2的某个次方。并且乘完之后要前一个数小于后一个数。
思路
观察一下发现,比如20 * ai < 21 * ai+1,把两边都除2,其实就是ai < 2 * ai+1,后面每一个位置的前后关系都是这样。也就是原数组里面也满足这样的ai < 2 * ai+1关系,就可以了
前缀和cnt数组,记录到i位置,最长可以到cnt[i]的满足条件的子数字
计算完cnt数组之后,数一下有多少个位置,大于等于k+1
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
void solve() {
int n, k; cin >> n >> k;
vector<int>a(n + 1), cnt(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
if ((a[i] << 1) > a[i - 1]) {
cnt[i] = cnt[i - 1] + 1;
}
else {
cnt[i] = 1;
}
}
long long res = 0;
for (int i = 1; i <= n; i++) {
if (cnt[i] >= k + 1) {
res++;
}
}
ans.push_back(res);
//for (int i = 1; i <= n; i++) {
// cout << cnt[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;
}
H. Gambling(前缀和)
题意
知道每一轮获奖的数字是什么,但是只准备在某个区间,一直选一个数字。问选哪个数字收益,在哪个区间选,收益最大
思路
这题也是看了别人的想法的,直接orz了这思路
很显然就可以想到是要求某个数字,在某个区间,这个数字出现的次数,比其他数字出现的总次数大。大多少。最大可以大多少。
这时候有一个方法就是,对于每一个数字x,都将原数组转化,把x看作1,其他数字看作-1。这时候再求前缀和数组sum,就可以知道这个数字x,在哪个[l,r]区间上的答案最大。
但是又可以对这个sum数组优化,就是搞一个map<int,vector<int>>,里面记录某个数字x的所有出现过的位置的下标。
sum前缀和数组就转化为sum[i] = sum[i-1] - (v[i] - v[i-1] - 1) + 1。
其中v就是记录了x所有出现位置的数组
减去(v[i] - v[i-1] - 1)就是说,v[i]是当前x在原数组中的位置,v[i-1]是x在原数组中上一次出现的位置,而这中间,就都看作是-1了。
最后+1是把当前v[i]这个位置的1给加上
其他的在代码里面说
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
vector<long long>ans;
void solve() {
int n; cin >> n;
map<int, vector<int>>mp;
vector<int>a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
mp[a[i]].push_back(i);
}
int res = 1, resNum = a[1], l = 1, r = 1;
// x是假设的答案,v是x对应原数组的下标数组
for (auto [x, v] : mp) {
vector<int>sum(v.size());
// 针对x做一个前缀和数组,把x看作1,其他数看作-1
// 从x第一次出现的位置作为前缀和数组的起点,因为如果要选x,不可能在x第一次出现的位置之前开始赌
sum[0] = 1;
for (int i = 1; i < sum.size(); i++) {
sum[i] = sum[i - 1] - (v[i] - v[i - 1] - 1) + 1;
}
// 每个区间的贡献是sum[r] - sum[l] + 1
// 也就是要让sum[r]尽量大(遍历所有x出现的位置)
// 要让sum[l]尽可能小,如果sum[r]已经遍历过了,而且比sum[l]小,那么l就可以更新到r
int i = 0;
// 因为前缀和数组是直接把所有x的下标挑出来做的前缀和数组
// 所以前缀和数组的每一个位置,都在原数组中对应了x的位置
// 每一个l和r,都是以x为左右端点的区间
for (int j = 0; j < v.size(); j++) {
int temp = sum[j] - sum[i] + 1; // sum[i]包含了自己的v[i],但是减掉了,加回1
if (temp > res) { // 更新答案
res = temp;
resNum = x;
l = v[i];
r = v[j];
}
if (sum[i] > sum[j])i = j; // 让左端点的前缀和更小
}
}
cout << resNum << " " << l << " " << r << 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号