C. Cookies and Greedy Takahashi
分别对负数和非负数维护一个递减的队列和一个递增的队列
代码实现
#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<int> l, r;
rep(i, n) {
int a;
cin >> a;
if (a < 0) l.push_back(a);
else r.push_back(a);
}
sort(l.begin(), l.end(), greater<>());
sort(r.begin(), r.end());
queue<ll> ql, qr;
for (int x : l) ql.push(x);
for (int x : r) qr.push(x);
const ll INF = 1e18;
ql.push(-INF);
qr.push(INF);
ll x = 0, ans = 0;
rep(k, n) {
if (x-ql.front() <= qr.front()-x) {
ans += x-ql.front();
x = ql.front();
ql.pop();
}
else {
ans += qr.front()-x;
x = qr.front();
qr.pop();
}
}
cout << ans << '\n';
return 0;
}
D. Chargers
假设在时间 \(t_{in}\) 插入了一只初始电量为 \(w\) 的电池。
在随后的任意时刻 \(t \ge t_{in}\),该电池的电量为:$$\min(V, w + (t - t_{in})) = \min(V, (w - t_{in}) + t)$$注意到对于任何电池,在时刻 \(t\),其电量仅由常量值 \(K = w - t_{in}\) 决定:
- 由于 \(f(K) = \min(V, K + t)\) 是关于 \(K\) 的单调递增函数;
- 因此,在任意时刻 \(t\),当前电量最高的电池必定对应 \(K = w - t_{in}\) 最大的电池。
我们可以维护一个大根堆存储所有已插入电池的 \(K\) 值。
代码实现
#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 Q, V;
cin >> Q >> V;
priority_queue<ll> q;
rep(qi, Q) {
int type, t;
cin >> type >> t;
if (type == 1) {
ll w;
cin >> w;
q.push(w-t);
}
else {
if (!q.size()) cout << -1 << '\n';
else {
ll ans = min<ll>(V, q.top()+t); q.pop();
cout << ans << '\n';
}
}
}
return 0;
}
E. Sum of Square of Sum
设选出的 \(K\) 个球的集合为 \(S\),其得分为:$$\left( \sum_{i \in S} A_i \right)^2 = \sum_{i \in S} A_i^2 + 2 \sum_{\overset{i, j \in S}{i < j}} A_i A_j$$考虑所有 \(\binom{N}{K}\) 种选择方案的总和:
- 单个元素的平方项 \(A_i^2\):每个球 \(A_i\) 会在 \(\binom{N-1}{K-1}\) 种选法中出现,因此这部分的总和为:$$\binom{N-1}{K-1} \sum_{i=1}^N A_i^2$$
- 交叉项 \(2 A_i A_j\):每对不同的球 \((A_i, A_j)\) 会在 \(\binom{N-2}{K-2}\) 种选法中同时出现(当 \(K \ge 2\) 时)。注意到:$$2 \sum_{1 \le i < j \le N} A_i A_j = \left( \sum_{i=1}^N A_i \right)^2 - \sum_{i=1}^N A_i^2$$
因此交叉项的总和为:$$\binom{N-2}{K-2} \left( \left( \sum_{i=1}^N A_i \right)^2 - \sum_{i=1}^N A_i^2 \right)$$
令 \(S_1 = \sum\limits_{i=1}^N A_i\),\(S_2 = \sum\limits_{i=1}^N A_i^2\),则最终答案为:$$\binom{N-1}{K-1} \cdot S_2 + \binom{N-2}{K-2} \cdot (S_1^2 - S_2)$$
代码实现
#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;
struct modinv {
int n; vector<mint> d;
modinv(): n(2), d({0,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(-d[mint::mod()%n]*(mint::mod()/n)), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} invs;
struct modfact {
int n; vector<mint> d;
modfact(): n(2), d({1,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(d.back()*n), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} facts;
struct modfactinv {
int n; vector<mint> d;
modfactinv(): n(2), d({1,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(d.back()*invs(n)), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} ifacts;
mint comb(int n, int k) {
if (n < k || k < 0) return 0;
return facts(n)*ifacts(k)*ifacts(n-k);
}
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n);
rep(i, n) cin >> a[i];
mint s, s2;
rep(i, n) s += a[i];
rep(i, n) s2 += mint(a[i])*a[i];
mint ans;
ans += s2*comb(n-1, k-1);
ans += (s*s-s2)*comb(n-2, k-2);
cout << ans.val() << '\n';
return 0;
}
F. Concat (maximize)
将全部 \(N\) 个字符串按长度从大到小排序(长度相同时按字典序降序排序)。
直接选取排序后的前 \(K\) 个字符串。这 \(K\) 个字符串能提供理论上最大的总位数。
- 拼接顺序:对选出的 \(K\) 个字符串,按自定义比较规则排序(若 \(A+B > B+A\),则 \(A\) 应该排在 \(B\) 前面),然后顺序拼接。
若选出的 \(\text{Top} K\) 个字符串全是零(例如 \(00, 000\)),拼接去零后值为 \(0\) 。
- 此时若剩余的 \(N-K\) 个字符串中存在非零数,应放弃 \(\text{Top} K\) 中最短的那个字符串(即第 \(K\) 个字符串),从剩余字符串中挑选一个非零且数值最大的字符串补入,重新按 \(A+B > B+A\) 拼接。
比较上述两种情况拼接并去除前导零后的结果(先比较长度,长度相同比字典序),较大的即为最终答案。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector<string> s(n);
rep(i, n) cin >> s[i];
auto cmp = [&](const string& a, const string& b) {
if (a.size() == b.size()) return a > b;
return a.size() > b.size();
};
sort(s.begin(), s.end(), cmp);
string ans = "0";
auto upd = [&](vector<string> s) {
sort(s.begin(), s.end(), [&](auto string& a, auto& string& b) {
return a+b > b+a;
});
string now;
for (string a : s) now += a;
reverse(now.begin(), now.end());
while (now.size() > 1 and now.back() == '0') now.pop_back();
reverse(now.begin(), now.end());
if (cmp(now, ans)) ans = now;
};
upd(vector<string>(s.begin(), s.begin()+k));
{
string best = "0";
for (int i = k; i < n; ++i) {
if (stoll(best) < stoll(s[i])) best = s[i];
}
vector<string> t(s.begin(), s.begin()+(k-1));
t.push_back(best);
upd(t);
}
cout << ans << '\n';
return 0;
}
G. Caeser Syllables
一个“音节”对应一个元音连续段。一个位置 \(i\) 是某个元音连续段的起点,当且仅当:
- \(A'_i\) 是元音;
- \(i = 1\) 或者 \(A'_{i-1}\) 不是元音。
因此,对于循环平移 \(k\):
- \(i = 1\) 时,贡献为 \(1\) 当且仅当 \(V[(A_1 + k) \bmod K] = 1\);
- \(i > 1\) 时,相邻元素对 \((A_{i-1}, A_i)\) 产生 \(1\) 的贡献当且仅当 \(V[(A_{i-1} + k) \bmod K] = 0\) 且 \(V[(A_i + k) \bmod K] = 1\)。
于是,总音节数公式为:
记 \(C[u][v]\) 为相邻对 \((A_{i-1}, A_i) = (u, v)\) 出现的次数。
对于某个循环偏移 \(k\),字符对 \((u, v)\) 构成“01”对的充要条件是:$$V[(u + k) \bmod K] = 0 \quad \text{且} \quad V[(v + k) \bmod K] = 1$$
对于给定的 \(u\)(要求 \(V[(u+k) \bmod K] = 0\)),需要求所有满足 \(V[(v+k) \bmod K] = 1\) 的 \(v\) 的 \(C[u][v]\) 之和。这本质上就是 \(C[u]\) 与 \(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;
using ll = long long;
using ull = unsigned long long;
int main() {
int n, k, m; ull seed;
cin >> n >> k >> seed >> m;
vector<int> b(m), v(k);
rep(i, m) cin >> b[i];
rep(i, k) cin >> v[i];
vector<int> c(k*k);
ull st = seed;
int a0 = 0, pre = 0;
rep(i, n) {
int val = 0;
if (i < m) val = b[i];
else {
ull x = (((st>>18)^st)>>27)%(1ull<<32);
ull r = st>>59;
ull y = ((x>>r)+(x<<(32-r)))%(1ull<<32);
val = y%k;
st = st*6364136223846793005 + 2026081520260815;
}
if (i == 0) {
a0 = val;
pre = val;
}
else {
c[pre*k+val]++;
pre = val;
}
}
vector<ll> ans(k);
rep(i, k) if (v[(a0+i)%k]) ans[i]++;
vector<int> nb(2*k);
rep(i, 2*k) nb[i] = v[i%k];
vector<int> na(k);
rep(i, k) {
bool ok = false;
rep(j, k) {
if (c[i*k+j]) {
ok = true;
break;
}
}
if (!ok) continue;
rep(ki, k) na[ki] = c[i*k+(k-1-ki)];
auto p = convolution(na, nb);
rep(ki, k) if (!v[(i+ki)%k]) ans[ki] += p[k-1+ki];
}
rep(i, k) cout << ans[i] << '\n';
return 0;
}
浙公网安备 33010602011771号