C. 2x2 Placing
set<pair<int, int>>
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int main() {
int n, m;
cin >> n >> m;
int ans = 0;
set<P> st;
rep(i, m) {
int r, c;
cin >> r >> c;
bool ok = true;
rep(dr, 2)rep(dc, 2) {
if (st.count(P(r+dr, c+dc))) ok = false;
}
if (!ok) continue;
rep(dr, 2)rep(dc, 2) {
st.emplace(r+dr, c+dc);
}
ans++;
}
cout << ans << '\n';
return 0;
}
D. Teleport Maze
暴力 \(\operatorname{bfs}\) 的复杂度为 \(O(H^2W^2)\)
考虑优化:
注意到,使用同一个字母的传送器两次显然是没用的,所以如果你使用了一次传送器,接下来你就不能再使用那个字母了!
时间复杂度可以降为 \(O(HW)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
const int di[] = {-1, 0, 1, 0};
const int dj[] = {0, 1, 0, -1};
int main() {
int h, w;
cin >> h >> w;
vector<string> s(h);
rep(i, h) cin >> s[i];
vector<vector<P>> warps(256);
rep(i, h)rep(j, w) warps[s[i][j]].emplace_back(i, j);
const int INF = 1001001001;
vector dist(h, vector<int>(w, INF));
queue<P> q;
auto push = [&](int i, int j, int d) {
if (dist[i][j] != INF) return;
dist[i][j] = d;
q.emplace(i, j);
};
push(0, 0, 0);
vector<bool> done(256);
while (q.size()) {
auto [i, j] = q.front(); q.pop();
int d = dist[i][j];
rep(v, 4) {
int ni = i+di[v], nj = j+dj[v];
if (ni < 0 or ni >= h or nj < 0 or nj >= w) continue;
if (s[ni][nj] == '#') continue;
push(ni, nj, d+1);
}
if (s[i][j] != '.' and !done[s[i][j]]) {
for (auto [ni, nj] : warps[s[i][j]]) {
push(ni, nj, d+1);
}
done[s[i][j]] = true;
}
}
int ans = dist[h-1][w-1];
if (ans == INF) ans = -1;
cout << ans << '\n';
return 0;
}
E. Minimum Swap
考虑排列的环分解
单次交换操作的效果:
- 当交换元素属于不同环时 \(\to\) 合并两个环
- 当交换元素属于同一个环时 \(\to\) 将该环分裂为两个
最终是要变成 \(N\) 个自环,所以只能增加环,那么就只能交换同一个环里的元素
代码实现
#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> p(n);
rep(i, n) cin >> p[i], p[i]--;
ll ans = 0;
vector<bool> used(n);
rep(i, n) if (!used[i]) {
int v = i, c = 0;
while (!used[v]) {
used[v] = true;
v = p[v];
c++;
}
ans += ll(c-1)*c/2;
}
cout << ans << '\n';
return 0;
}
F. Starry Landscape Photo
枚举 \(b=1, 2, \cdots, N\),统计包含第 \(b\) 亮的这个星星的位置 \(X_b\) 的区间数
对于这个区间数,可以用树状数组或平板电视来找到 \(X_b\) 的左右两边亮度排名不超过 \(b\) 的星星个数,用乘法原理算一下贡献即可
代码实现
#include <bits/extc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using namespace __gnu_pbds;
using ll = long long;
template<class T>
using Tree = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
int main() {
int n;
cin >> n;
vector<int> b(n);
rep(i, n) cin >> b[i], b[i]--;
vector<int> x(n);
rep(i, n) x[b[i]] = i;
ll ans = 0;
Tree<int> t;
rep(i, n) {
int l = t.order_of_key(x[i]), r = i-l;
ans += ll(l+1)*(r+1);
t.insert(x[i]);
}
cout << ans << '\n';
return 0;
}
G. Linear Inequation
数位 \(\text{dp}\) 或生成函数
可以参考abc300ex的官方题解
数位 \(\text{dp}\):
把每个 \(x_i\) 按二进制位分解,把“每一位上哪些 \(i\) 选 \(1\)”视作一次 \(\text{0/1}\) 子集和问题(同一位的分布对所有位都一样),再用按位合并(除以 \(2\) 并根据 \(m\) 的当前位做奇偶调整)来模拟进位和保持 \(\leqslant M\) 的限制,从而在多位上递推计数。
代码实现
#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() {
int n; ll m;
cin >> n >> m;
vector<int> a(n);
rep(i, n) cin >> a[i];
const int X = 20000;
vector<mint> dp(X+1);
dp[0] = 1;
while (m) {
rep(i, n) {
for (int j = X; j >= a[i]; --j) {
dp[j] += dp[j-a[i]];
}
}
vector<mint> old(X+1);
swap(dp, old);
rep(j, X+1) {
int nj = j;
if ((nj&1) != (m&1)) nj++;
dp[nj/2] += old[j];
}
m >>= 1;
}
cout << dp[0].val() << '\n';
return 0;
}
生成函数:
考虑在序列 \(A\) 的开头添加一个元素 \(A_0=1\)
这样原问题就转化成了统计满足以下条件的长度为 \(N+1\) 的序列 \(x = (x_0, x_1, \cdots, x_n)\) 的序列个数:
- \(\displaystyle\sum_{i=0}^N A_ix_i = M\)
考虑使用生成函数
记 \(f_i(x) = x^0 + x^{A_i} + x^{2A_i} + \cdots\)
那么最后的答案就是 \([x^M] \prod f_i\)
\(f_i(x) = \frac{1}{1-x^{A_i}}\)
\(\Rightarrow \prod f_i = \dfrac{1}{\prod\limits_{i=0}^N (1-x^{A_i})}\)
注意到这里的 \(M\) 很大,所以只能用 \(\text{Bostan-Mori}\) 求解
时间复杂度是 \(O(N\log N \log M)\)
代码实现
#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;
using fps = vector<mint>;
mint bostan_mori(fps p, fps q, ll n) {
while (n) {
fps iq = q;
for (int i = 1; i < q.size(); i += 2) iq[i] = -iq[i];
p = convolution(p, iq);
iq = convolution(q, iq);
rep(i, q.size()) q[i] = iq[i*2];
for (int i = n&1; i < p.size(); i += 2) p[i/2] = p[i];
p.resize((p.size()+!(n&1))/2);
n >>= 1;
}
return p[0] / q[0];
}
int main() {
int n; ll m;
cin >> n >> m;
fps p = {1}, q = {1, -1};
rep(i, n) {
int a;
cin >> a;
fps f(a+1);
f[0] = 1; f[a] = -1;
q = convolution(q, f);
}
mint ans = bostan_mori(p, q, m);
cout << ans.val() << '\n';
return 0;
}
浙公网安备 33010602011771号