C. Sugoroku Destination
注意到 \(i \leqslant A_i \leqslant N\),所以从 \(i\) 出发一定会走到某个自环
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
rep(i, n) a[i]--;
vector<int> ans(n);
for (int i = n-1; i >= 0; --i) {
if (a[i] == i) ans[i] = i;
else ans[i] = ans[a[i]];
}
rep(i, n) cout << ans[i]+1 << " \n"[i == n-1];
return 0;
}
D. Reconstruct Chocolate
贪心地“逆向还原”出一次可能的放置过程:因为原过程每次把手里的一块矩形沿着整格边界切成两块,放下一块、拿起另一块,最终在桌上放下的 \(N\) 块组成一个 \(H×W\) 的矩形——所以在逆向构造时每一步被放到桌上的那块必然要么高度等于当前剩余的 \(H\)(即占满整列,高度方向未被切分),要么宽度等于当前剩余的 \(W\)(占满整行,宽度方向未被切分)。
代码实现
#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 H, W, n;
cin >> H >> W >> n;
vector<int> h(n), w(n);
rep(i, n) cin >> h[i] >> w[i];
map<int, vector<int>> mph, mpw;
rep(i, n) mph[h[i]].push_back(i);
rep(i, n) mpw[w[i]].push_back(i);
vector<bool> used(n);
auto get = [&](map<int, vector<int>>& mp, int val) {
vector<int>& is = mp[val];
while (is.size() and used[is.back()]) is.pop_back();
if (is.size() == 0) return -1;
return is.back();
};
vector<P> ans(n);
int r = 1, c = 1;
auto put = [&](int i) {
ans[i] = P(r, c);
used[i] = true;
if (h[i] == H) W -= w[i], c += w[i];
else H -= h[i], r += h[i];
};
rep(ni, n) {
int i = get(mph, H);
if (i == -1) i = get(mpw, W);
put(i);
}
for (auto [r, c] : ans) cout << r << ' ' << c << '\n';
return 0;
}
E. Many LCMs
先求出所有数的 \(\text{LCM}\),对每个数分解质因数,对于每个质因子取它最大的指数,然后做乘积即可
对于每个素因子,只需考虑它最大的两个指数 \(--\) 如果去掉的那个数的某个素因子的指数和 \(\text{LCM}\) 里的一样,只需将它的最大指数替换成次大指数
代码实现
#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 P = pair<int, int>;
using mint = modint998244353;
struct Sieve {
int n;
vector<int> f, primes;
Sieve(int n=1): n(n), f(n+1) {
f[0] = f[1] = -1;
for (ll i = 2; i <= n; ++i) {
if (f[i]) continue;
primes.push_back(i);
f[i] = i;
for (ll j = i*i; j <= n; j += i) {
if (!f[j]) f[j] = i;
}
}
}
bool isPrime(int x) { return f[x] == x; }
vector<int> factorList(int x) {
vector<int> res;
while (x != 1) {
res.push_back(f[x]);
x /= f[x];
}
return res;
}
vector<P> factor(int x) {
vector<int> fl = factorList(x);
if (fl.size() == 0) return {};
vector<P> res(1, P(fl[0], 0));
for (int p : fl) {
if (res.back().first == p) {
res.back().second++;
}
else {
res.emplace_back(p, 1);
}
}
return res;
}
vector<ll> factorList(ll x) {
vector<ll> res;
for (int p : primes) {
while (x%p == 0) {
res.push_back(p);
x /= p;
}
}
if (x != 1) res.push_back(x);
return res;
}
vector<pair<ll, int>> factor(ll x) {
vector<ll> fl = factorList(x);
if (fl.size() == 0) return {};
vector<pair<ll, int>> res(1, P(fl[0], 0));
for (ll p : fl) {
if (res.back().first == p) {
res.back().second++;
}
else {
res.emplace_back(p, 1);
}
}
return res;
}
} sieve(1e7);
void solve() {
int n;
cin >> n;
vector<int> a(n);
rep(i, n) cin >> a[i];
map<int, vector<int>> es;
rep(i, n) {
auto fs = sieve.factor(a[i]);
for (auto [p, e] : fs) {
es[p].push_back(e);
}
}
mint base = 1;
for (auto& p : es) {
p.second.push_back(0);
sort(p.second.begin(), p.second.end(), greater<>());
base *= mint(p.first).pow(p.second[0]);
}
auto get = [&](vector<int>& a, int x) {
int y = a[0];
if (x == y) y = a[1];
return a[0]-y;
};
rep(i, n) {
auto fs = sieve.factor(a[i]);
mint ans = base;
for (auto [p, e] : fs) {
ans /= mint(p).pow(get(es[p], e));
}
cout << ans.val() << " \n"[i == n-1];
}
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
F. Exactly K Steps 2
最短路 \((\min, +)\) 矩阵快速幂
双倍经验:Graph Paths II
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using G = vector<vector<ll>>;
inline void chmin(ll& x, ll y) { if (x > y) x = y; }
int main() {
int n, k;
cin >> n >> k;
G c(n, vector<ll>(n));
rep(i, n)rep(j, n) cin >> c[i][j];
const ll INF = 1e18;
auto mul = [&](const G& a, const G& b) -> G {
G c(n, vector<ll>(n, INF));
rep(i, n)rep(j, n)rep(k, n) {
chmin(c[i][j], a[i][k]+b[k][j]);
}
return c;
};
G g(n, vector<ll>(n, INF));
rep(i, n) g[i][i] = 0;
while (k) {
if (k&1) g = mul(g, c);
c = mul(c, c);
k >>= 1;
}
rep(i, n) cout << g[i][i] << '\n';
return 0;
}
F. Knight Placement
二分图最大独立集
求最小割即可
代码实现
#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;
inline void chmin(ll& x, ll y) { if (x > y) x = y; }
int main() {
int n, a, b;
cin >> n >> a >> b;
vector<string> s(n);
rep(i, n) cin >> s[i];
int N = n*n;
vector<vector<int>> to(N);
rep(i, n)rep(j, n) if (s[i][j] == '.') {
rep(i1, 2) {
rep(i2, 2) {
rep(i3, 2) {
int ni = i+a, nj = j+b;
if (0 <= ni and ni < n and 0 <= nj and nj < n and s[ni][nj] == '.') {
to[i*n+j].push_back(ni*n+nj);
}
a = -a;
}
b = -b;
}
swap(a, b);
}
}
vector<int> col(N, -1);
rep(i, N) {
auto f = [&](auto& f, int v, int c=0) -> void {
if (col[v] != -1) return;
col[v] = c;
for (int u : to[v]) f(f, u, !c);
};
f(f, i);
}
const int INF = 1001001001;
int sv = N, tv = sv+1;
mf_graph<int> g(tv+1);
rep(i, N) {
if (col[i] == 0) {
g.add_edge(sv, i, 1);
for (int j : to[i]) g.add_edge(i, j, INF);
}
else {
g.add_edge(i, tv, 1);
}
}
g.flow(sv, tv);
auto d = g.min_cut(sv);
rep(i, n)rep(j, n) if (s[i][j] == '.') {
int v = i*n+j;
if (d[v] == (col[v]==0)) s[i][j] = 'o';
}
rep(i, n) cout << s[i] << '\n';
return 0;
}
浙公网安备 33010602011771号