A. Equally

模拟

代码实现
a, b, c = sorted(map(int, input().split()))
ans = a == b == c or a+b == c
print('Yes' if ans else 'No')

B. Santa Claus 1

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int h, w, si, sj;
cin >> h >> w >> si >> sj;
--si; --sj;
vector<string> s(h);
rep(i, h) cin >> s[i];
string t;
cin >> t;
int ans = 0;
for (char c : t) {
int ni = si, nj = sj;
if (c == 'U') ni--;
if (c == 'D') ni++;
if (c == 'L') nj--;
if (c == 'R') nj++;
if (s[ni][nj] == '#') continue;
si = ni; sj = nj;
if (s[si][sj] == '@') {
s[si][sj] = '.';
++ans;
}
}
cout << si+1 << ' ' << sj+1 << ' ' << ans << '\n';
return 0;
}

C. Illuminate Buildings

枚举间隔 w,起点为 [0,w1],在这样的子序列中找最长相同连续段即可
时间复杂度为 O(N2)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<int> h(n);
rep(i, n) cin >> h[i];
int ans = 0;
for (int w = 1; w <= n; ++w) {
rep(si, w) {
vector<int> a;
for (int i = si; i < n; i += w) {
a.push_back(h[i]);
}
int val = -1, len = 0;
for (int x : a) {
if (val == x) len++;
else val = x, len = 1;
ans = max(ans, len);
}
}
}
cout << ans << '\n';
return 0;
}

D. Santa Claus 2

对于每个房子的位置,可以对它的每种横坐标维护它对应的所有纵坐标,对每种纵坐标维护它对应的所有横坐标(这个是老套路了

每次移动需要完成以下的需求:

  • 获取当前方向上的第一个房子的位置
  • 从对应的两个横纵集合里删掉这个位置

可以用 map<int, set<int>> 来维护

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using D = map<ll, set<ll>>;
int main() {
int n, m; ll sx, sy;
cin >> n >> m >> sx >> sy;
D xs, ys;
rep(i, n) {
int x, y;
cin >> x >> y;
xs[y].insert(x);
ys[x].insert(y);
}
int ans = 0;
auto f = [&](D& xs, D& ys, ll y, ll l, ll r) {
if (l > r) swap(l, r);
auto& st = xs[y];
while (1) {
auto it = st.lower_bound(l);
if (it == st.end()) break;
if (*it > r) break;
ys[*it].erase(y);
st.erase(it);
ans++;
}
};
rep(mi, m) {
char d; int c;
cin >> d >> c;
ll nx = sx, ny = sy;
if (d == 'U') ny += c;
if (d == 'D') ny -= c;
if (d == 'R') nx += c;
if (d == 'L') nx -= c;
if (sy == ny) f(xs, ys, sy, sx, nx);
else f(ys, xs, sx, sy, ny);
sx = nx, sy = ny;
}
cout << sx << ' ' << sy << ' ' << ans << '\n';
return 0;
}

E. Snowflake Tree

不难看出最终的树只有三层
先枚举中心点,然后对中心点周围的点的度数做降序排序,再枚举中心点的每个邻接点,因为已经做降序排序了,那么从当前点开始往左的所有兄弟节点的度数就一定大于等于当前点,假设已经枚举到了第 i 个点,那么就能分成 i 组,每组的点数都是 deg[i](构成一颗子树),再加上中心点就是最终留下来的点数,那么用 n 减去保留下来的点数就是需要删掉的点数了

时间复杂度为 O(NlogN)

代码实现
#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<vector<int>> to(n);
rep(i, n-1) {
int a, b;
cin >> a >> b;
--a; --b;
to[a].push_back(b);
to[b].push_back(a);
}
vector<int> deg(n);
rep(i, n) deg[i] = to[i].size();
int ans = n;
rep(v, n) {
vector<int> d;
for (int u : to[v]) d.push_back(deg[u]);
ranges::sort(d, greater<>());
rep(i, deg[v]) {
int now = d[i]*(i+1) + 1;
ans = min(ans, n-now);
}
}
cout << ans << '\n';
return 0;
}

F. Visible Buildings

先对所有点 (Xi,Hi) 跑凸包
然后取凸包上每条边的直线在 y 轴上的截距的最大值就是答案
如果所有截距 <0,则无解

代码实现
#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; }
V& operator/=(ll s) { x /= s; y /= s; return *this; }
V& operator/(ll s) const { return V(*this) /= s; }
ll dot(const V& v) const { return x*v.x + y*v.y; }
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;
cin >> n;
double ans = -100;
bool all = true;
vector<V> conv;
rep(i, n) {
ll x, h;
cin >> x >> h;
V p(x, h);
while (conv.size() >= 2) {
V a = conv.back(), b = conv.end()[-2];
if ((p.y-a.y)*(p.x-b.x) < (p.y-b.y)*(p.x-a.x)) break;
conv.pop_back();
}
if (conv.size() >= 1) {
V a = conv.back();
ll dy = a.y-p.y, dx = p.x-a.x;
double y = double(dy*p.x + p.y*dx)/dx;
if ((a.y-p.y)*p.x >= -p.y*(p.x-a.x)) all = false;
ans = max(ans, y);
}
conv.push_back(p);
}
if (all) puts("-1");
else printf("%.10f\n", ans);
return 0;
}

G. Counting Buildings

考虑对每个建筑按高度从到到小进行放置
假设已经放置了一些建筑,现在考虑插入一个新的新的建筑,如果插在最左边,那么 LL+1,如果插在最右边,那么 RR+1,插在其他位置没有影响

dp[i][j] 表示已经插了 i 个数且当前的 LR=j 的方案数
对于 dp[i]dp[i+1]
1 种插法的贡献为 +1,有 1 种插法的贡献为 1,有 i1 种插法的贡献为 0

写成生成函数就是 [xj]i=0N2(x1+x1+i),等价于 [xj+(N1)]i=0N2(x2+ix+1)

然后跑分治 FFT 即可

代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using mint = modint998244353;
using fps = vector<mint>;
int main() {
int n, k;
cin >> n >> k;
queue<fps> q;
q.push(fps(1, 1));
rep(i, n-1) {
fps f(3, 1);
f[1] = i;
q.push(f);
}
while (q.size() >= 2) {
auto a = q.front(); q.pop();
auto b = q.front(); q.pop();
q.push(convolution(a, b));
}
auto f = q.front();
mint ans = f[k+(n-1)];
cout << ans.val() << '\n';
return 0;
}