C. Lock All Doors
我们要先走到“最左边还开着的门”和“最右边还开着的门”那里,所以沿途遇到的所有关着的门都要先开着。然后再从两端开始,依次把门关上就行了!
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, R;
cin >> n >> R;
--R;
vector<int> L(n);
rep(i, n) cin >> L[i];
int l = R, r = R;
rep(i, n) {
if (L[i] == 1) continue;
l = min(l, i-1);
r = max(r, i);
}
int ans = 0;
for (int i = l+1; i <= r; ++i) {
ans += L[i]+1;
}
cout << ans << '\n';
return 0;
}
D. Long Waiting
模拟
用小根堆来维护每批顾客对应的二元组 (离店时间,人数),同时维护一下当前空出来的人数
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<ll, int>;
int main() {
int n, k;
cin >> n >> k;
priority_queue<P, vector<P>, greater<P>> q;
int empty = k;
ll now = 0;
rep(i, n) {
int a, b, c;
cin >> a >> b >> c;
if (now < a) {
while (q.size() and q.top().first < a) {
empty += q.top().second;
q.pop();
}
now = a;
}
while (empty < c) {
now = q.top().first;
empty += q.top().second;
q.pop();
}
empty -= c;
q.emplace(now+b, c);
cout << now << '\n';
}
return 0;
}
E. Sum of Subarrays
算贡献
原式就是求 \((A_{L_i}, A_{L_i+1}, \cdots, A_{R_i})\) 的所有连续子段的元素和的总和。也就是说,对于任意 \(x \in [L_i, R_i]\),它被包含的连续子段数等于 \((x-L_i+1) \times (R_i-x+1)\),因此答案就是 \(\sum A_x \times (x-L_i+1) \times (R_i-x+1)\)
记 \(l = L_i-1, r = R_i+1\)
\((x-L_i+1) \times (R_i-x+1) = (x-l) \times (r-x)\)
将 \((x-l) \times (r-x)\) 展开得到 \(-x^2 + (l+r)x - lr\)
于是,答案就变成了 \(-\sum x^2 A_x + (l+r)\sum xA_x - lr\sum A_x\)
只需预处理一下 \(A_x, xA_x, x^2A_x\) 的前缀和,就能做到 \(O(1)\) 回答了
代码实现
#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, q;
cin >> n >> q;
vector<int> a(n);
rep(i, n) cin >> a[i];
vector s(3, vector<ll>(n+1));
rep(k, 3) {
rep(i, n) {
ll x = a[i];
rep(j, k) x *= i;
s[k][i+1] = s[k][i] + x;
}
}
rep(qi, q) {
int l, r;
cin >> l >> r;
--l;
ll ans = 0;
ans -= (s[2][r]-s[2][l]);
ans += (s[1][r]-s[1][l])*(l-1+r);
ans -= (s[0][r]-s[0][l])*(l-1)*r;
cout << ans << '\n';
}
return 0;
}
F. Loud Cicada
容斥
对于任意一组蝉,“至少这几只蝉同时出现的次数”就等于它们周期的最小公倍数
对所有子集都这么算一遍,再做快速莫比乌斯变换,就能得到“恰好只有这几只蝉出现的次数”!
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ull = unsigned long long;
const ull INF = 1ull<<60;
int main() {
int n, m; ull y;
cin >> n >> m >> y;
vector<ull> a(n);
rep(i, n) cin >> a[i];
int n2 = 1<<n;
vector<ull> d(n2);
rep(s, n2) {
ull x = 1;
rep(i, n) if (s>>i&1) {
ull t = a[i]/gcd(x, a[i]);
if (INF/x < t) x = INF; else x *= t;
}
d[s] = y/x;
}
rep(i, n)rep(s, n2) if (s>>i&1) d[s^1<<i] -= d[s];
ull ans = 0;
rep(s, n2) if (__builtin_popcount(s) == m) ans += d[s];
cout << ans << '\n';
return 0;
}
G. Small Multiple 2
对 \(S\) 出现位置以下的各位进行全搜索。由于只需调整最低位就能使其成为 \(K\) 的倍数,所以最低位最多试 \(0∼9\) 即可。
接着,在 \(S\) 出现位置以上的高位取值中,只要找出存在合适低位的最小高位值即可。该“存在合适低位”等价于对应的 \(\bmod K\) 余数落在某个区间,借助
floor_sum 可以高效统计满足条件的高位方案数,因而可通过二分搜索确定最小值。
浙公网安备 33010602011771号