C. Reverse Permutation
按 \(i\) 的升序依次“添加 \(i\)” \(\to\) “执行操作 \(i\)”。用 std::deque 维护当前数组,同时维护是否处于反转状态,并以 \(O(1)\) 时间处理反转。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
deque<int> a;
bool rev = false;
rep(i, n) {
if (rev) a.push_front(i+1);
else a.push_back(i+1);
if (s[i] == 'o') rev = !rev;
}
if (rev) ranges::reverse(a);
rep(i, n) cout << a[i] << " \n"[i == n-1];
return 0;
}
还有另一种做法:
从后往前决定每个数字放到最终数组的哪一端,利用一个左右指针在数组两端填入位置,维护一个翻转标志 rev 表示当前方向。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
vector<int> a(n);
int l = 0, r = n-1; bool rev = false;
for (int i = n-1; i >= 0; --i) {
if (s[i] == 'o') rev = !rev;
if (!rev) a[r--] = i; else a[l++] = i;
}
rep(i, n) cout << a[i]+1 << " \n"[i == n-1];
return 0;
}
D. X to Y
这题其实就是把 P13016 的父节点简化了
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
void solve() {
ll x, y, k;
cin >> x >> y >> k;
int ans = 0;
while (x != y) {
if (x > y) x /= k; else y /= k;
ans++;
}
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
E. Digit Circus
数位dp
记 dp[i][j][k][s] 表示
- 已经处理了前 \(i\) 位
- \(j=0\) 表示当前已构造的前缀与 \(N\) 的前 \(i\) 位完全相同,\(j=1\) 表示已经完全小于 \(N\) 的前缀
- 当前前缀对应的数模 \(3\) 的余数为 \(k\)
- 当前前缀对应的数集为 \(s\)
这里时间给的比较宽裕,完全可以用 std::map 来实现
代码实现
#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 mint = modint998244353;
int main() {
string N;
cin >> N;
using T = tuple<int, int, int>;
map<T, mint> dp;
dp[T(0, 0, 0)] = 1;
for (char c : N) {
c -= '0';
map<T, mint> old;
swap(dp, old);
for (auto [t, x] : old) {
auto [j, k, s] = t;
rep(d, 10) {
int nj = j, nk = (k*10+d)%3, ns = s|1<<d;
if (nj == 0 and d > c) continue;
if (d < c) nj = 1;
if (s == 0 and d == 0) ns = 0;
dp[T(nj, nk, ns)] += x;
}
}
}
mint ans;
for (auto [t, x] : dp) {
auto [j, k, s] = t;
int cnt = 0;
if (k == 0) cnt++;
if (s>>3&1) cnt++;
if (__builtin_popcount(s) == 3) cnt++;
if (cnt == 1) ans += x;
}
ans -= 1;
cout << ans.val() << '\n';
return 0;
}
F. Sjeltzer?
本题的核心突破口在于饮品 ID 的长度只有 \(6\) 位。这意味着所有可能的 ID 组合总共只有 \(10^6\) 种。
由于每一位数字之间是相互独立的,我们可以把一个 \(6\) 位数的 ID 看作是六维空间中的一个坐标点,每个维度的取值范围都是 \(0\) 到 \(9\)。题目要求的查询,本质上是在这个六维空间中,求一个“超矩形区域” \([x_1, y_1] \times [x_2, y_2] \times \dots \times [x_6, y_6]\) 内所有点权值的总和。
为了快速回答大量询问,预处理高维前缀和,并在查询时利用多维容斥原理进行 \(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;
cin >> n;
int m = 6;
vector<int> ten(m+1, 1);
rep(i, m) ten[i+1] = ten[i]*10;
int M = ten[m];
vector<ll> d(M);
rep(i, n) {
int s, v;
cin >> s >> v;
d[s] += v;
}
rep(k, m) {
rep(i, M) {
if (i/ten[k]%10 == 9) continue;
d[i+ten[k]] += d[i];
}
}
int q;
cin >> q;
rep(qi, q) {
int x, y;
cin >> x >> y;
ll ans = 0;
rep(s, 1<<m) {
int r = 0, sign = 1;
rep(i, m) {
int c = y/ten[i]%10;
if (s>>i&1) {
sign = -sign;
c = min(c, x/ten[i]%10-1);
}
if (c < 0) sign = 0;
else r += c*ten[i];
}
if (r < 0) continue;
ans += d[r]*sign;
}
cout << ans << '\n';
}
return 0;
}
浙公网安备 33010602011771号