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';
}
posted @ 2024-06-12 06:49  Lu_xZ  阅读(515)  评论(1编辑  收藏  举报