codeforces round 799
799
A
计算 \(b, c, d\) 中有几个数比 \(a\) 大就好
B
题意
一次只能删任意两个数,最后只能留下不同的数,想要留下的数最多
思路
- 最好的情况,最后留下的个数为 \(cnt\) (种类数)
- 计算 \(n - cnt\) 。偶数则可以全部删除,奇数就要从 \(cnt\) 中删一个来配对
C
求对角线交点的坐标
误区 : 并不是一行或一列只有一个 # 的就是交点 (除掉边界之后)
以行为例,只有 上一行和下一行都有两个 #,这一行有一个的才是
D
傻傻地不断判断是否为回文,直到和初状态重合
bool check (int x, int y) {
string s1, s2;
if (x < 10) s1 = '0' + to_string (x);
else s1 = to_string (x);
if (y < 10) s2 = '0' + to_string (y);
else s2 = to_string (y);
reverse (s2.begin (), s2.end());
if (s1 == s2) return true;
return false;
}
void solve () {
string s; int x;
cin >> s >> x;
int dh = x / 60, dm = x % 60;
int sh = (s[0] - '0') * 10 + (s[1] - '0');
int sm = (s[3] - '0') * 10 + (s[4] - '0');
int cnt = check (sh, sm) ? 1 : 0;
if (x == 1440) {
cout << cnt << '\n';
return;
}
int hh = sh, mm = sm;
while(1) {
hh = hh + dh, mm = dm + mm;
if (mm >= 60) hh += (mm / 60), mm %= 60;
hh %= 24;
if (hh == sh && mm == sm) break;
if (check (hh, mm)) cnt ++;
}
cout << cnt << '\n';
}
E
题意
一次只能删 第一个 或 最后一个数,希望用最少的步骤使最后为指定和
思考
❓无法判断是删第一个数好,还是最后一个数好
转换思路
-
想要用最少步骤 --> 删去的数最少
-
只能删两边 --> 最后的序列连续,要么在中间,要么在两边
那么,要求的就是 满足指定和这个条件的,最长连续子序列
❓如何求最长连续子序列 且 满足条件
前缀和 和 二分
以一个点为起始点,利用二分找到满足条件的后面的点,如果最大的那个都不满足,就可以结束了
void solve()
{
int n, s, ma = 0; cin >> n >> s;
vector<int> a(n + 1), ss(n + 1);
for(int i = 1; i <= n; i++) {
cin >> a[i];
ss[i] = ss[i - 1] + a[i];
}
if(ss[n] == s) {cout << 0 << '\n'; return;}
if(ss[n] < s) {cout << -1 << '\n'; return;}
for(int i = 1; i <= n; i++) {
int u = s + ss[i - 1], y;
auto q = upper_bound(ss.begin(), ss.end(), u);
if(q == ss.end()) {
if(ss[n] == u) y = n;
else break;
}
y = q - ss.begin() - 1;
ma = max(ma, y - i + 1);
}
cout << n - ma << '\n';
}
F
找到三个数相加所得数的个位数是3
傻方法 --- 手动枚举
最大的情况 \(9 + 9 + 9\) 是 \(27\),只有三种情况 : \(3, 13, 23\)
稍微省力一点的方法 --- 自动枚举
将所有个位数 出现次数小于等于 \(3\) 的数都放进容器,然后一个个匹配,看是否满足
附上手动枚举的代码
void solve()
{
int n; cin >> n;
map<int, int> mp;
for(int i = 0; i < n; i++) {
string s;
int w; cin >> w;
s = to_string(w);
int u = s.size();
int q = s[u - 1] - '0';
mp[q]++;
}
if(mp[0]) {
if(mp[0] >= 2 && mp[3] >= 1) {cout << "YES\n"; return;}
if(mp[0] >= 1) {
if((mp[1] >= 1 && mp[2] >= 1) || (mp[4] >= 1 && mp[9] >= 1) || (mp[5] >= 1 && mp[8] >= 1) || (mp[6] >= 1 && mp[7] >= 1)) {cout << "YES\n"; return;}
}
}
if(mp[1] >= 1) {
if((mp[1] >= 3) || (mp[3] >= 1 && mp[9] >= 1) || (mp[4] >= 1 && mp[8] >= 1) || (mp[5] >= 1 && mp[7] >= 1) || (mp[6] >= 2)) {cout << "YES\n"; return;}
}
if(mp[2] >= 1) {
if((mp[2] >= 2 && mp[9] >= 1) || (mp[3] >= 1 && mp[8] >= 1) || (mp[4] >= 1 && mp[7] >= 1) || (mp[5] >= 1 && mp[6] >= 1)) {cout << "YES\n"; return;}
}
if(mp[3] >= 1) {
if((mp[3] >= 2 && mp[7] >= 1) || (mp[4] >= 1 && mp[6] >= 1) || mp[5] >= 2) {cout << "YES\n"; return;}
}
if((mp[4] >= 2 && mp[5] >= 1) || (mp[5] >= 1 && mp[9] >= 2) || (mp[6] >= 1 && mp[8] >= 1 && mp[9] >= 1) || (mp[7] >= 2 && mp[9] >= 1) || (mp[7] >= 1 && mp[8] >= 2)) {cout << "YES\n"; return;}
cout << "NO\n";
}
G
将题目所给公式化简 -- 每两个连续的数之间,后面都是前面的两倍以上
不用担心不相连的数不满足这种关系,2是1的两倍以上,3是2的两倍以上,那么3肯定是1的四倍以上,直接写就好
void solve()
{
int n, k; cin >> n >> k;
int cnt = 0, l = 0, r = 0;
vector<ll> a(n);
for(int i = 0; i < n; i++) cin >> a[i];
for(int i = 1; i < n; i++) {
if(a[i - 1] < a[i] * 2) r++;
else cnt += max(0, r - l + 1 - k), l = i, r = l;
}
cnt += max(0, r - l + 1 - k);
cout << cnt << '\n';
}
H
此题本人看了题解才明白,很妙了
记录每一个数出现的下标,循环遍历每一个数去作为x
使用前缀和计算最大收益,x记为1,非x记为-1
计算前缀和时,当前位置的收益为 上一个x的位置的收益 - 上一个x到此处的距离(这中间都是非x,都是-1)再加上1(自身是个x)
计算完之后寻找一个区间使得和最大,如果s[l] > s[r] ,就说明要变更区间了,l = r
void solve()
{
int n; cin >> n;
vector<int> a(n + 1);
map<int, vector<int>> v;
for (int i = 1; i <= n; i++) {
cin >> a[i];
v[a[i]].push_back(i);
}
int l = 1, r = 1, res = a[1], cnt = 1;
for (auto& [q, w] : v) {
int u = w.size();
vector<int> s(u + 1);
for (int i = 1; i < u; i++) {
s[i] = s[i - 1] - (w[i] - w[i - 1] - 1) + 1;
}
int i = 0;
for (int j = 1; j < u; j++) {
int z = s[j] - s[i] + 1;
if (z > cnt) {
cnt = z, res = q, l = w[i], r = w[j];
}
if (s[i] > s[j]) i = j;
}
}
cout << res << ' ' << l << ' ' << r << '\n';
}
``

浙公网安备 33010602011771号