Codeforces Round #762 (Div.3)
#762(Div.3)
B. Squares and Cubes
题意
给定数字 \(n\) ,求 \(1 \sim n\) 中有多少平方数和立方数。
分析
由于只需要找平方数和立方数,我们可以暴力找出所有平方数和立方数,复杂度为 \(log n\) ,注意判重。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200010;
void solve ()
{
map<int, bool> ex; // 判重数组
int n, ans = 0; cin >> n;
for (int i = 1; i <= n / i; i ++ )
{
ans ++ ;
ex[i * i] = 1;
}
for (int i = 1; i * i <= n / i; i ++ )
if (!ex[i * i * i]) ans ++ ;
cout << ans << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}
C. Wrong Addition
题意
给定加法法则为:从右到左计算,每次把各位相加的结果填充到结果而不是进位。
即: \(88 + 99 = 1717\) 。
给出加数、和,求另一个加数。
分析
从加数、和的最后一位开始,如果在当前位,加数小于和,那么一定存在进位,否则一定不存在进位。
不存在解的情况:
- 在某一位上,当前加数的位数大于和的位数
- 在进位的时候发现和的前一位不是1
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define all(a) begin(a),end(a)
using namespace std;
void solve ()
{
string a, s, ans; cin >> a >> s;
for (int p = s.size() - 1, pa = a.size() - 1; p >= 0; )
{
// 存在某一位,使得当前加数的数量大于和
if (pa > p) return cout << -1 << endl, void();
if (pa < 0) // 加数已经结束了,直接把和加上去
{
ans += s[p--];
continue;
}
if (s[p] >= a[pa]) // 不存在进位
{
ans += s[p] - a[pa] + '0';
p -- ; pa -- ;
}
else // 存在进位
{
// 存在进位,那么和的前一位一定是1
if (s[p - 1] != '1') return cout << -1 << endl, void();
ans += s[p] + 10 - a[pa] + '0';
p -= 2; pa -- ;
}
}
reverse(all(ans));
while(ans[0] == '0') ans = ans.substr(1);
cout << ans << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}
D. New Year's Problem
题意
给出矩阵 \(p_{ij}\) ,\(1 \le i \le m, \ 1 \le j \le n\) 。
选择至多 \(n-1\) 行,然后在选择的行中,每一列挑选一个数字,求选择的数字的最小值的最大值。
分析
要求最小值的最大值,可以想到用二分答案。
由于要选择至多 \(n-1\) 行,那么我们只需要找出有 \(2\) 个数字在同一行即可。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define int long long
using namespace std;
void solve ()
{
int n, m; cin >> m >> n;
vector<vector<int>> p(m, vector<int>(n));
rep(i, 0, m-1) rep(j, 0, n-1) cin >> p[i][j];
auto check = [&] (int mid) -> bool
{
map<int, bool> ex; // 存储满足的商店
bool may = 0; // may表示存在两列可以选择同一行
// 使用may是为了防止后面的列中不存在大于等于mid的数字
for (int i = 0; i < n; i ++ )
{
bool f = 0; // 是否存在至少一个数字满足大于等于mid
for (int j = 0; j < m; j ++ )
if (p[j][i] >= mid)
{
if (ex[j]) may = 1;
ex[j] = 1; f = 1;
}
if (!f) return false;
}
return may;
};
int l = 1, r = 1e9 + 10;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << r << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}
E. MEX and increments
题意
给出 \(n\) 个数字,每次操作可以把一个数字加1。可以操作任意次。
问:对于数字 \(i \ (0 \le i \le n)\) ,是否存在一种操作,可以满足序列的 \(mex\) 为 \(i\) 。输出它的最小操作次数。
分析
对于数字 \(i\) ,假设 \(cnt([0, i-1]) < i\) ,那么一定不能把前面 \(i\) 个数字填满,一定无解。
否则,我们可以选择把离它最近的位置,填到 \(i-1\) 的位置,使得序列的 \(mex\) 为 \(i\) 。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
using namespace std;
using PII = pair<int, int>;
#define int long long
const int N = 200010;
int cnt[N];
void solve ()
{
int n; cin >> n;
vector<PII> mct; // mct 用来动态记录cnt
for (int i = 0; i <= n; i ++ ) cnt[i] = 0;
for (int i = 1, x; i <= n && cin >> x; i ++ ) ++ cnt[x];
int precnt = 0, d = 0;
// 对于0,特殊判断
cout << cnt[0] << ' ';
if (cnt[0] > 1) mct.push_back({0, cnt[0] - 1});
for (int i = 1; i <= n; i ++ )
{
precnt += cnt[i-1];
if (precnt < i)
{
while(i ++ <= n) cout << -1 << " \n" [i - 1 == n];
return ;
}
// 如果i-1的位置没有元素,要把i-1铺掉
if (!cnt[i-1])
{
while (!mct[mct.size() - 1].second) mct.pop_back();
-- mct[mct.size() - 1].second;
d += i - 1 - mct[mct.size() - 1].first;
}
cout << d + cnt[i] << ' ';
if (cnt[i] > 1) mct.push_back({i, cnt[i] - 1});
}
cout << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}