Codeforces Round 952 (Div. 4) (A - H2)
Codeforces Round 952 (Div. 4)
A
void solve() {
string s, t; cin >> s >> t;
swap(s[0], t[0]);
cout << s << ' ' << t << '\n';
}
B
原数据范围枚举足矣。
进一步分析性质:\(x + 2x + \cdots \lfloor\dfrac{n}{x} \rfloor x = x\lfloor\dfrac{n}{x} \rfloor\dfrac{(1 + \lfloor\frac{n}{x} \rfloor)}{2}\),对较大的 \(n\) 来说,答案取决于 \(\lfloor\dfrac{n}{x} \rfloor\) 的大小,显然取 \(x = 2\) 最优。
对于较小的 \(n\),打标验证只有 \(3\) 是例外。
void solve() {
int n;
std::cin >> n;
if (n == 3) {
std::cout << 3 << "\n";
} else {
std::cout << 2 << "\n";
}
}
// by jiangly
C
设当前前缀和为 \(s\),检查是否存在 \(x = s - x\)。
void solve() {
int n; cin >> n;
ll s = 0, ans = 0;
map<ll, int> mp;
for(int i = 1, x; i <= n; ++ i) {
cin >> x;
mp[x] = 1;
s += x;
if(s % 2 == 0) {
ans += mp[s / 2];
}
}
cout << ans << '\n';
}
D
题目保证了是完整的圆。找到左右边界即可。
void solve() {
int n, m; cin >> n >> m;
int mx[2] = {1, 1}, mi[2] = {n, m};
for(int i = 0; i < n; ++ i) {
string s; cin >> s;
for(int j = 0; j < m; ++ j) {
if(s[j] == '#') {
mx[0] = max(mx[0], i + 1);
mi[0] = min(mi[0], i + 1);
mx[1] = max(mx[1], j + 1);
mi[1] = min(mi[1], j + 1);
}
}
}
cout << (mx[0] + mi[0]) / 2 << ' ' << (mx[1] + mi[1]) / 2 << '\n';
}
E
枚举 \(k = a \times b \times c\)。可以 \(O(xy)\) 枚举 \(a, b\),再检查 \(c\)。
void solve() {
ll x, y, z, k; cin >> x >> y >> z >> k;
ll ans = 0;
for(int a = 1; a <= x; ++ a) {
for(int b = 1; b <= y; ++ b) {
ll c = k / a / b;
if(c * a * b != k || c > z) continue;
ans = max(ans, (x - a + 1) * (y - b + 1) * (z - c + 1));
}
}
cout << ans << '\n';
}
F
二分时间 \(t\)。对于一种攻击,第一天是一定要的,剩下可以攻击 \(\lfloor \dfrac{t - 1}{c_i}\rfloor\) 次。
bool check(ll v) {
__int128 s = 0;
for(int i = 1; i <= n; ++ i) {
s += a[i] * __int128((v - 1) / c[i] + 1);
}
return s >= h;
}
void solve() {
cin >> h >> n;
for(int i = 1; i <= n; ++ i) cin >> a[i];
for(int i = 1; i <= n; ++ i) cin >> c[i];
ll l = 1, r = 1e18;
while(l < r) {
ll mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << l << '\n';
}
G
\(d(kn) = kd(n)\) 成立当且仅当 \(n\) 乘 \(k\) 后不进位,证明待补。
记 \(x = \lfloor\dfrac{9}{k}\rfloor\)。对于一个长度为 \(i\) 的数字 \(n\),最高位 \(\in [1, x]\),剩余位 \(\in [0, x]\),总个数为 \((x - 1)x^{i - 1}\)。
符合条件的数字长度 \(\in [l + 1, r]\)。则合法数字个数为 \((x - 1)(x^l + \cdots + x^{r -1}) = x^r - x^l\)。
void solve() {
cin >> l >> r >> k;
if(k >= 10) {
cout << 0 << '\n';
return;
}
ll x = 9 / k + 1;
ll ans = (qpow(x, r) - qpow(x, l)) % P;
if(ans < 0) ans += P;
cout << ans << '\n';
}
H1
只考虑行,列同理处理。
定义行 \(i\) 的额外贡献 \(r_i\) 为不属于 \(i\),但把 \(i\) 涂黑后属于一个连通块的节点数量。
对于一个大小为 \(g\) 的连通块 \(G\),会对所有贯穿 \(G\) 的行以及与 \(G\) 相邻的行产生额外贡献。
\(r_i = g - cnt_x(i)\),\(cnt_x(i)\) 表示属于 \(G\) 且横坐标为 \(i\) 的节点数量。
最后答案为 \(m + \max(r_i)\)。
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
void solve() {
int n, m; cin >> n >> m;
vector<string> g(n);
for(int i = 0; i < n; ++ i) {
cin >> g[i];
}
vector<int> r(n, 0);
vector<int> c(m, 0);
vector id(n, vector<int>(m, 0));
auto ck = [&](int x, int y) {
return x >= 0 && x < n && y >= 0 && y < m;
};
int idx = 0;
vector<int> cnt_r(n, 0);
vector<int> cnt_c(m, 0);
vector<int> st_r(n, 0);
vector<int> st_c(m, 0);
for(int i = 0; i < n; ++ i) {
for(int j = 0; j < m; ++ j) {
if(!id[i][j] && g[i][j] == '#') {
id[i][j] = ++ idx;
queue<pair<int, int>> q;
vector<pair<int, int>> a;
q.eb(i, j);
while(!q.empty()) {
auto [x, y] = q.front(); q.pop();
++ cnt_r[x];
++ cnt_c[y];
a.pb(x, y);
for(int k = 0; k < 4; ++ k) {
int nx = x + dx[k], ny = y + dy[k];
if(ck(nx, ny) && !id[nx][ny] && g[nx][ny] == '#') {
q.eb(nx, ny);
id[nx][ny] = idx;
}
}
}
for(auto [x, y] : a) {
if(!st_r[x]) {
st_r[x] = 1;
r[x] += a.size() - cnt_r[x];
}
if(!st_c[y]) {
st_c[y] = 1;
c[y] += a.size() - cnt_c[y];
}
for(int k = 0; k < 4; ++ k) {
int nx = x + dx[k], ny = y + dy[k];
if(ck(nx, ny)) {
if(!st_r[nx]) {
st_r[nx] = 1;
r[nx] += a.size() - cnt_r[nx];
}
if(!st_c[ny]) {
st_c[ny] = 1;
c[ny] += a.size() - cnt_c[ny];
}
}
}
}
for(auto [x, y] : a) {
st_r[x] = st_c[y] = 0;
-- cnt_r[x];
-- cnt_c[y];
for(int k = 0; k < 4; ++ k) {
int nx = x + dx[k], ny = y + dy[k];
if(ck(nx, ny)) {
st_r[nx] = st_c[ny] = 0;
}
}
}
}
}
}
int ans = max(n, m);
for(int i = 0; i < n; ++ i) {
ans = max(ans, m + r[i]);
}
for(int j = 0; j < m; ++ j) {
ans = max(ans, n + c[j]);
}
cout << ans << '\n';
}
H2
和 H1 思路一致。
设 \(G\) 对行 \(i\) 的额外贡献为 \(r_i\),列 \(j\) 的额外贡献为 \(c_j\)。
把 \(i, j\) 染色后的连通块大小是否为 \(n + m - 1 + r_i + c_j\)?
如果一个 \(G\) 同时对 \(i, j\) 产生了额外贡献,应当被记入答案的贡献为 \(g - cnt_x(i) - cnt_y(j) + cnt_{xy}(i, j)\),实际记入 \((g - cnt_x(i)) + (g - cnt_y(i))\)。
重复计算了 \(g - cnt_{xy}(i, j)\)。
枚举 \(G\) 产生贡献的行 \(i\) 列 \(j\),把 \(g - cnt_{xy}(i, j)\) 加入 \(rc_{i, j}\) 表示 \(i, j\) 重复计算的个数。
连通块大小是 \(\sum g = O(nm)\) 的,而有贡献的 \(i \times j = (\text{连通块的行数 + 周围一圈}) \times (\text{连通块的列数 + 周围一圈}) = O(?)\),所以总复杂度 \(O(?)\)。
upd:最坏复杂度 \(O(n^3/6)\),可以恰好通过。
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
void solve() {
int n, m; cin >> n >> m;
vector<string> g(n);
for(int i = 0; i < n; ++ i) {
cin >> g[i];
}
vector<int> r(n, 0);
vector<int> c(m, 0);
vector rc(n, vector<int>(m, 0));
vector id(n, vector<int>(m, 0));
auto ck = [&](int x, int y) {
return x >= 0 && x < n && y >= 0 && y < m;
};
int idx = 0;
vector<int> cnt_r(n, 0);
vector<int> cnt_c(m, 0);
vector cnt_rc(n, vector<int>(m, 0));
vector<int> st_r(n, 0);
vector<int> st_c(m, 0);
for(int i = 0; i < n; ++ i) {
for(int j = 0; j < m; ++ j) {
if(!id[i][j] && g[i][j] == '#') {
id[i][j] = ++ idx;
queue<pair<int, int>> q;
vector<pair<int, int>> a;
q.eb(i, j);
while(!q.empty()) {
auto [x, y] = q.front(); q.pop();
++ cnt_r[x];
++ cnt_c[y];
++ cnt_rc[x][y];
a.pb(x, y);
for(int k = 0; k < 4; ++ k) {
int nx = x + dx[k], ny = y + dy[k];
if(ck(nx, ny) && !id[nx][ny] && g[nx][ny] == '#') {
q.eb(nx, ny);
id[nx][ny] = idx;
}
}
}
vector<int> X, Y;
for(auto [x, y] : a) {
if(!st_r[x]) {
st_r[x] = 1;
r[x] += a.size() - cnt_r[x];
X.pb(x);
}
if(!st_c[y]) {
st_c[y] = 1;
c[y] += a.size() - cnt_c[y];
Y.pb(y);
}
for(int k = 0; k < 4; ++ k) {
int nx = x + dx[k], ny = y + dy[k];
if(ck(nx, ny)) {
if(!st_r[nx]) {
st_r[nx] = 1;
r[nx] += a.size() - cnt_r[nx];
X.pb(nx);
}
if(!st_c[ny]) {
st_c[ny] = 1;
c[ny] += a.size() - cnt_c[ny];
Y.pb(ny);
}
}
}
}
for(auto x : X) {
for(auto y : Y) {
if(ck(x, y)) {
rc[x][y] += a.size() - cnt_rc[x][y];
}
}
}
for(auto [x, y] : a) {
st_r[x] = st_c[y] = 0;
-- cnt_r[x];
-- cnt_c[y];
-- cnt_rc[x][y];
for(int k = 0; k < 4; ++ k) {
int nx = x + dx[k], ny = y + dy[k];
if(ck(nx, ny)) {
st_r[nx] = st_c[ny] = 0;
}
}
}
}
}
}
int ans = n + m - 1;
for(int i = 0; i < n; ++ i) {
for(int j = 0; j < m; ++ j) {
int tmp = n + m - 1;
ans = max(ans, tmp + r[i] + c[j] - rc[i][j]);
}
}
cout << ans << '\n';
}