A. Leap Year
模拟
代码实现
import calendar
y = int(input())
if calendar.isleap(y):
print(366)
else:
print(365)
B. Second Best
模拟
代码实现
n = int(input())
a = list(map(int, input().split()))
print(a.index(sorted(a)[-2])+1)
C. Transportation Expenses
二分
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n; ll m;
cin >> n >> m;
vector<int> a(n);
rep(i, n) cin >> a[i];
auto f = [&](int x) {
ll s = 0;
rep(i, n) s += min(x, a[i]);
return s <= m;
};
const int INF = 1001001001;
if (f(INF)) puts("infinite");
else {
int ac = 0, wa = INF;
while (abs(ac-wa) > 1) {
int wj = (ac+wa)/2;
if (f(wj)) ac = wj; else wa = wj;
}
cout << ac << '\n';
}
return 0;
}
D. AtCoder Janken 3
如果你考虑“从前到后遍历能赢就赢”的贪心的话,会发现过不了样例3
考虑dp
记 dp[i][j]
表示到第 \(i\) 次操作为止且最后一次出的是 \(j\) 时所能获胜的最大次数
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
const int INF = 1001001001001;
vector dp(n+1, vector<int>(3, -INF));
rep(i, 3) dp[0][i] = 0;
for (int i = 1; i <= n; ++i) {
int x = 0;
if (s[i-1] == 'R') x = 0;
if (s[i-1] == 'P') x = 1;
if (s[i-1] == 'S') x = 2;
rep(j, 3) {
int val = 0;
if (j == (x+1)%3) val = 1;
if (j == (x+2)%3) continue;
rep(pj, 3) {
if (j == pj) continue;
dp[i][j] = max(dp[i][j], dp[i-1][pj]+val);
}
}
}
int ans = ranges::max(dp[n]);
cout << ans << '\n';
return 0;
}
E. Xor Sigma Problem
按位算贡献,具体分析见 题解
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
vector<int> s(n+1);
rep(i, n) s[i+1] = s[i]^a[i];
ll ans = 0;
rep(k, 30) {
int one = 0;
rep(i, n+1) if (s[i]>>k&1) one++;
ans += one*ll(n+1-one)*(1ll<<k);
}
rep(i, n) ans -= a[i];
cout << ans << '\n';
return 0;
}
F. Takahashi on Grid
假设从 \((i, y)\) 出发,走到 \(x=i+1\) 时 的最少移动次数以及对应的 \(y\) 坐标分别为 \(f_i(y)\) 和 \(g_i(y)\),那么 \(f_i\) 和 \(g_i\) 的复合可以在 \(O(1)\) 的时间计算得到,然后上线段树。
G. AtCoder Office
根号分治
先假设一个阈值 \(D\),再令第 \(i\) 个人对应的区间个数为 \(C_i\)
容易看出所有人的总的区间个数最多为 \(M\) 个
当 \(C_A \leqslant D\) 且 \(C_B \leqslant D\) 时,可以直接暴力双指针做,时间复杂度为 \(O(C_A + C_B)\),参考 LC986
当 \(A\) 和 \(B\) 中有一个区间的长度 \(> D\),不失一般性地,我们假设 \(A\) 的长度大于 \(D\),那么这样的 \(A\) 显然不超过 \(\lfloor\frac{M}{D}\rfloor\) 个
然后做一下预处理,记 f(i, j)
表示询问 \(i, j\) 的答案,其中 \(C_i > D\),暴力转移可以在 \(\mathcal{O}(\frac{M^2}{D})\) 时间内完成
根据均值不等式可知,\(\frac{M^2}{D} + QD \geqslant \sqrt{M^2Q}\),等式成立当且仅当 \(D = \frac{M}{\sqrt{Q}}\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int main() {
int n, m;
cin >> n >> m;
vector<P> records;
vector<vector<int>> ts(n);
rep(i, m) {
int t, p;
cin >> t >> p;
--p;
records.emplace_back(t, p);
ts[p].push_back(t);
}
vector<bool> many(n);
rep(i, n) many[i] = ts[i].size() > 400;
vector<vector<int>> d(n);
rep(i, n) if (many[i]) {
d[i] = vector<int>(n);
int s = 0, pt = 0; bool in = false;
vector<int> sign(n, -1);
for (auto [t, p] : records) {
if (in) s += t-pt;
if (p == i) {
in = !in;
}
else {
d[i][p] += s*sign[p];
sign[p] *= -1;
}
pt = t;
}
}
int q;
cin >> q;
rep(qi, q) {
int a, b;
cin >> a>> b;
--a; --b;
if (many[b]) swap(a, b);
int ans = 0;
if (many[a]) ans = d[a][b];
else {
bool ain = false, bin = false;
int ai = 0, bi = 0, pt = 0;
while (ai < ts[a].size() and bi < ts[b].size()) {
int ta = ts[a][ai], tb = ts[b][bi];
int t = min(ta, tb);
if (ain and bin) ans += t-pt;
pt = t;
if (ta < tb) ++ai, ain = !ain;
else ++bi, bin = !bin;
}
}
cout << ans << '\n';
}
return 0;
}