C. Upgrade Required
开一个桶来维护每种版本的电脑数量,一开始每个桶中的电脑数都是 \(1\)
用变量 \(r\) 来维护“第一个可能非空的版本号”,并且 \(r\) 只会单调递增。每次操作把 \(r\) 指向的连续若干个桶(直到 \(x\))合并到 \(y\),并把这些痛清空。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> cnt(n+1);
for (int i = 1; i <= n; ++i) cnt[i] = 1;
int r = 1;
rep(qi, q) {
int x, y;
cin >> x >> y;
int ans = 0;
while (r <= x) {
ans += cnt[r];
cnt[r] = 0;
r++;
}
cnt[y] += ans;
cout << ans << '\n';
}
return 0;
}
D. Pop and Insert
固定某个极大同字符连续段,和它不在一段的相同字符的代价是 \(2\),剩下其他不同字符的代价是 \(1\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void solve() {
int n;
string s;
cin >> n >> s;
vector<int> cnt(2);
vector<pair<char, int>> d(1, {s[0], 0});
for (char c : s) {
cnt[c-'0']++;
if (d.back().first == c) {
d.back().second++;
}
else {
d.emplace_back(c, 1);
}
}
int ans = n*2;
for (auto [c, num] : d) {
int i = c-'0';
vector<int> ncnt = cnt;
ncnt[i] -= num;
int now = ncnt[i]*2 + ncnt[i^1];
ans = min(ans, now);
}
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
E. Closest Moment
将两人的位置做相对位移(把 Aoki 的位置视为参照,把原点看作 Aoki 的位置)——这样问题变为“一个点(Takahashi 相对于 Aoki 的位置)在时间上沿线段-线段折线运动,求到原点的最小距离”。
因为速度相同且是匀速行直线,故相对位移随时间是分段线性的,只需在关键时间点(\(0\),短者到达时,长者到达时)取位置,得到最多两条线段的折线。
最小距离即是原点到这条折线的最短距离,可逐段求点到线段距离并取最小值。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
const double eps = 1e-9;
struct V {
double x, y;
V(double x=0, double 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*=(double s) { x *= s; y *= s; return *this; }
V operator*(double s) const { return V(*this) *= s; }
V& operator/=(double s) { x /= s; y /= s; return *this; }
V operator/(double s) const { return V(*this) /= s; }
double dot(const V& v) const { return x*v.x + y*v.y; }
double cross(const V& v) const { return x*v.y - v.x*y; }
double norm2() const { return x*x + y*y; }
double norm() const { return sqrt(norm2()); }
int ort() const { // orthant
if (abs(x) < eps and abs(y) < eps) return 0;
if (y > 0) return x > 0 ? 1 : 2;
else return x < 0 ? 3 : 4;
}
bool operator<(const V& v) const {
int o = ort(), vo = v.ort();
if (o != vo) return o < vo;
return cross(v) > 0;
}
};
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;
}
struct Line {
V s, t;
Line(V s=V(0,0), V t=V(0,0)):s(s),t(t){}
V dir() const { return t-s;}
// V normalize() const { return dir().normalize();}
double norm() const { return dir().norm();}
/* +1: s-t,s-p : ccw
* -1: s-t,s-p : cw
* +2: t-s-p
* -2: s-t-p
* 0: s-p-t */
int ccw(const V& p) const {
if (dir().cross(p-s) > eps) return +1;
if (dir().cross(p-s) < -eps) return -1;
if (dir().dot(p-s) < -eps) return +2;
if (dir().norm()+eps < (p-s).norm()) return -2;
return 0;
}
bool touch(const Line& l) const {
int a = ccw(l.s)*ccw(l.t), b = l.ccw(s)*l.ccw(t);
return !a || !b || (a == -1 && b == -1);
}
V divpoint(double p) const {
return s*(1-p) + t*p;
}
double distSP(V p) const {
if ((p-s).dot(t-s) < eps) return (s-p).norm();
if ((p-t).dot(s-t) < eps) return (t-p).norm();
return abs((s-p).cross(t-p))/(t-s).norm();
}
};
void solve() {
Line t, a;
cin >> t.s >> t.t >> a.s >> a.t;
if (t.norm() < a.norm()) swap(t, a);
double tlen = t.norm();
double alen = a.norm();
double ans = 1e18;
{
Line l;
l.s = t.s-a.s;
l.t = t.divpoint(alen/tlen) - a.t;
ans = min(ans, l.distSP(V(0, 0)));
}
{
Line l;
l.s = t.divpoint(alen/tlen) - a.t;
l.t = t.t-a.t;
ans = min(ans, l.distSP(V(0, 0)));
}
printf("%.10f\n", ans);
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
F. Clearance
使用支持区间加法和区间最小值查询的带延迟标记的线段树来维护数组状态。
当某次操作使得某个元素变为负数时,先在线段树上通过二分查找定位该位置,然后将其置为 \(+\infty\) 。同时再用树状数组维护 \(+\infty\) 值的个数,以支持查询区间内至少有 \(k\) 件的商品种类数。
代码实现
#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 ll = long long;
ll op(ll a, ll b) { return min(a, b); }
ll e() { return 1e18; }
ll mapping(ll f, ll x) { return x-f; }
ll composition(ll f, ll g) { return f+g; }
ll id() { return 0; }
int main() {
int n;
cin >> n;
vector<ll> a(n);
rep(i, n) cin >> a[i];
int q;
cin >> q;
lazy_segtree<ll, op, e, ll, mapping, composition, id> rem(a);
fenwick_tree<int> sold(n);
rep(qi, q) {
int l, r, k;
cin >> l >> r >> k;
--l;
ll ans = ll(r-l-sold.sum(l, r))*k;
rem.apply(l, r, k);
while (1) {
auto f = [&](ll x) { return x >= 0; };
int i = rem.max_right(l, f);
if (i >= r) break;
ans += rem.get(i);
rem.set(i, e());
sold.add(i, 1);
}
cout << ans << '\n';
}
return 0;
}
G. Range Knapsack Query
二区间合并(又叫“猫树分治”)
在这里,只需对左半区间做从右往左的 \(01\) 背包,以及对右半区间做从左往右的 \(01\) 背包
类似题:子集和(六)
(这题是YACS上的原题,不知道什么原因上了锁)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using Q = tuple<int, int, int, int>;
inline void chmax(ll& a, ll b) { if (a < b) a = b; }
int main() {
int n;
cin >> n;
vector<int> w(n), v(n);
rep(i, n) cin >> w[i] >> v[i];
int q;
cin >> q;
vector<Q> qs;
rep(qi, q) {
int l, r, c;
cin >> l >> r >> c;
--l;
qs.emplace_back(l, r, c, qi);
}
vector<ll> ans(q);
const int m = 500;
vector dp(n+1, vector<ll>(m+1));
auto f = [&](auto& f, int l, int r, vector<Q> qs) -> void {
int k = (l+r)/2;
dp[k] = vector<ll>(m+1);
for (int i = k-1; i >= l; --i) {
dp[i] = dp[i+1];
for (int j = m-w[i]; j >= 0; --j) {
chmax(dp[i][j+w[i]], dp[i][j]+v[i]);
}
}
for (int i = k; i < r; ++i) {
dp[i+1] = dp[i];
for (int j = m-w[i]; j >= 0; --j) {
chmax(dp[i+1][j+w[i]], dp[i+1][j]+v[i]);
}
}
vector<Q> ql, qr;
for (auto [nl, nr, c, qi] : qs) {
if (nr < k) ql.emplace_back(nl, nr, c, qi);
else if (nl > k) qr.emplace_back(nl, nr, c, qi);
else {
ll now = 0;
rep(j, c+1) chmax(now, dp[nl][j]+dp[nr][c-j]);
ans[qi] = now;
}
}
if (ql.size()) f(f, l, k, ql);
if (qr.size()) f(f, k, r, qr);
};
f(f, 0, n, qs);
rep(qi, q) cout << ans[qi] << '\n';
return 0;
}