C. On a Diet
维护一下长度为 \(M\) 的滑动窗口里吃掉的零食的总热量
代码实现
#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, m; ll k;
cin >> n >> m >> k;
vector<int> a(n);
rep(i, n) cin >> a[i];
vector<bool> eat(n);
ll sum = 0;
rep(i, n) {
if (i >= m and eat[i-m]) sum -= a[i-m];
sum += a[i];
if (sum <= k) {
eat[i] = true;
}
else {
sum -= a[i];
eat[i] = false;
}
}
rep(i, n) {
if (eat[i]) puts("Yes");
else puts("No");
}
return 0;
}
D. Bomber Mad
以所有的安全空格子为起点跑多源 \(\text{bfs}\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int di[] = {-1, 0, 1, 0};
int dj[] = {0, -1, 0, 1};
int main() {
int h, w, k;
cin >> h >> w >> k;
vector<string> s(h);
rep(i, h) cin >> s[i];
vector<bool> row(h), col(w);
rep(i, h)rep(j, w) {
if (s[i][j] == '#') row[i] = col[j] = true;
}
const int INF = 1001001001;
vector dist(h, vector<int>(w, INF));
queue<P> q;
auto push = [&](int i, int j, int d) {
if (dist[i][j] != INF) return;
dist[i][j] = d;
q.emplace(i, j);
};
rep(i, h)rep(j, w) {
if (!row[i] and !col[j]) push(i, j, 0);
}
while (q.size()) {
auto [i, j] = q.front(); q.pop();
rep(v, 4) {
int ni = i+di[v], nj = j+dj[v];
if (ni < 0 or nj < 0 or ni >= h or nj >= w) continue;
if (s[ni][nj] == '#') continue;
push(ni, nj, dist[i][j]+1);
}
}
int ans = 0;
rep(i, h)rep(j, w) if (dist[i][j] <= k) ++ans;
cout << ans << '\n';
return 0;
}
E. Odd Cycle
二分图判定
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void solve() {
int n, m;
cin >> n >> m;
vector<vector<int>> to(n);
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
to[a].push_back(b);
to[b].push_back(a);
}
vector<int> col(n, -1);
vector<int> vs;
auto f = [&](auto& f, int v, int c) -> bool {
if (col[v] != -1) {
if (col[v] != c) {
reverse(vs.begin(), vs.end());
while (vs.back() != v) vs.pop_back();
return true;
}
return false;
}
vs.push_back(v);
col[v] = c;
for (int u : to[v]) {
if (f(f, u, !c)) return true;
}
vs.pop_back();
return false;
};
f(f, 0, 0);
if (vs.size()) {
cout << vs.size() << '\n';
for (int v : vs) cout << v+1 << ' ';
cout << '\n';
}
else {
cout << -1 << '\n';
}
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
F. Centroid of a Slice
对顶点按顺序构成的简单多边形,设 \(a=\sum\limits_i p_i \times p_{i+1}\)(这里 \(×\) 表示向量叉积),则多边形有向面积 \(A=\frac{1}{2}a\)。
重心的分子可用边贡献累加得到:
\[\sum_i (p_i+p_{i+1})(p_i \times p_{i+1})
\]
最终重心为
\[C = \frac{1}{6A}\sum_i (p_i+p_{i+1})(p_i \times p_{i+1})
\]
由于 \(6A=3a\),如果用 \(a=\sum p_i×p_{i+1}\) ,则最后除以 \(3a\) 。
然后预处理一下前缀和即可。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
struct V {
ll x, y;
V(ll x=0, ll y=0): x(x), y(y) {}
V& operator+=(const V& v) { x += v.x; y += v.y; return *this; }
V operator+(const V& v) const { return V(*this) += v; }
V& operator-=(const V& v) { x -= v.x; y -= v.y; return *this; }
V operator-(const V& v) const { return V(*this) -= v; }
V& operator*=(ll s) { x *= s; y *= s; return *this; }
V operator*(ll s) const { return V(*this) *= s; }
ll cross(const V& v) const { return x*v.y - v.x*y; }
};
istream& operator>>(istream& is, V& v) {
is >> v.x >> v.y; return is;
}
ostream& operator<<(ostream& os, const V& v) {
os << "(" << v.x << "," << v.y << ")"; return os;
}
int main() {
int n, q;
cin >> n >> q;
vector<V> ps(n);
rep(i, n) cin >> ps[i];
rep(i, n) ps.push_back(ps[i]);
int n2 = n*2;
vector<ll> area(n2);
vector<V> cent(n2);
rep(i, n2-1) {
V p = ps[i], q = ps[i+1];
ll a = p.cross(q);
V c = p+q; c *= a;
area[i+1] = area[i] + a;
cent[i+1] = cent[i] + c;
}
rep(qi, q) {
int l, r;
cin >> l >> r;
--l; --r;
if (r < l) r += n;
ll a = area[r]-area[l];
V c = cent[r]-cent[l];
{
V p = ps[r], q = ps[l];
ll na = p.cross(q);
a += na;
c += (p+q)*na;
}
double x = c.x, y = c.y;
x /= a*3; y /= a*3;
printf("%.10f %.10f\n", x, y);
}
return 0;
}
G. Cascading Grid
把每个非 # 的格子看作图中的一个节点,节点的权值为:若是 + 则收益 \(+1\),若是 - 则收益 \(-1\) 。
选择某个格子并把它能到达的所有格子变成 # 的约束,等价于:一旦在选删除集里包含某个节点 \(u\),那么图中从 \(u\) 出发能到达的所有节点 \(v\) 也必须被删除。也就是删除集要对有向边的「可达性」向前闭合(如果 \(u\) 在删除集,且有边 \(u \to v\),则 \(v\) 也必须在删除集)。
这正是标准的最大权闭合子图问题:我们要选一个闭合(对有向边前向闭合)的点集,使得保留的点权和最大化。等价地,删除的点集(补集)要最小化被删去的正贡献减去删去的负贡献,能用最小割建模。
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int h, w;
cin >> h >> w;
vector<string> s(h);
rep(i, h) cin >> s[i];
const int INF = 1001001001;
int n = h*w;
int sv = n, tv = sv+1;
mf_graph<int> g(tv+1);
auto f = [&](int i, int j) { return i*w+j; };
int ans = 0;
rep(i, h)rep(j, w) if (s[i][j] != '#') {
if (s[i][j] == '+') ans++, g.add_edge(f(i, j), tv, 1);
else g.add_edge(sv, f(i, j), 1);
if (i and s[i-1][j] != '#') g.add_edge(f(i-1, j), f(i, j), INF);
if (j and s[i][j-1] != '#') {
g.add_edge(f(i, j-1), f(i, j), INF);
g.add_edge(f(i, j), f(i, j-1), INF);
}
}
ans -= g.flow(sv, tv);
cout << ans << '\n';
return 0;
}
浙公网安备 33010602011771号