#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;
}
