C. King's Summit
设所有点中行坐标极差为 \(\Delta R\),列坐标极差为 \(\Delta C\),则最短相遇时间为 \(\max(\lceil \frac{\Delta R}{2} \rceil, \lceil \frac{\Delta C}{2} \rceil)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n;
cin >> n;
const int INF = 1001001001;
int minR = INF, maxR = -INF;
int minC = INF, maxC = -INF;
rep(i, n) {
int r, c;
cin >> r >> c;
minR = min(minR, r); maxR = max(maxR, r);
minC = min(minC, c); maxC = max(maxC, c);
}
int ans = (max(maxR-minR, maxC-minC)+1)/2;
cout << ans << '\n';
return 0;
}
D. Substr Swap
只需要知道每个字符被交换次数的奇偶性,用差分计算区间和就能搞定!
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
string s, t;
cin >> s >> t;
vector<int> d(n+1);
rep(i, m) {
int l, r;
cin >> l >> r;
--l;
d[l]++; d[r]--;
}
rep(i, n) d[i+1] += d[i];
string ans;
rep(i, n) {
if (~d[i]&1) ans += s[i];
else ans += t[i];
}
cout << ans << '\n';
return 0;
}
E. Subarray Sum Divisibility
首先必须满足 \(A_1+A_2+\cdots + A_L\) 是 \(M\) 的倍数。接着让 \(A_2+A_3+\cdots + A_{L+1}\) 也是 \(M\) 的倍数,由于这两个和的差等于 \(A_{L+1}-A_1\),所以 \(A_{L+1}\) 和 \(A_1\) 关于模 \(M\) 必须同余才行!
同理可推出,关于模 \(L\) 同余下标的值,关于 \(M\) 也必须同余。因此,我们需要计算“将 \(A_k, A_{k+L}, A_{k+2L}, \cdots\)” 调整为模 \(M\) 等于 \(m\) 的最小操作次数,并记为 cost[k][m]。
最后,为了让 \(A_1+A_2+\cdots + A_L\) 成为 \(M\) 的倍数,定义 dp[i][m] 为 \(m_1 + m_2 + \cdots + m_i\) 模 \(M\) 等于 \(m\) 时,\(\mathrm{cost}[1][m_1] + \mathrm{cost}[2][m_2] + \cdots + \mathrm{cost}[i][m_i]\) 的最小值进行 \(\mathrm{dp}\),最终答案就是 \(\mathrm{dp}[L][0]\)!
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
inline void chmin(int& a, int b) { if (a > b) a = b; }
int main() {
int n, m, l;
cin >> n >> m >> l;
vector<int> a(n);
rep(i, n) cin >> a[i];
vector cost(l, vector<int>(m));
rep(i, n) {
int g = i%l;
rep(j, m) cost[g][(a[i]+j)%m] += j;
}
const int INF = 1001001001;
vector<int> dp(m, INF);
dp[0] = 0;
rep(i, l) {
vector<int> old(m, INF);
swap(dp, old);
rep(j, m)rep(k, m) {
chmin(dp[(j+k)%m], old[j]+cost[i][k]);
}
}
cout << dp[0] << '\n';
return 0;
}
F. All Included
考虑向后添加字符构建字符串,可以设计出如下的 \(\mathrm{dp}\) 状态
记 dp[i][s][j] 表示长度为 \(i\) 的串中,包含的字符串集合为 \(s\),以字符串 \(j\) 结尾
其中 \(j\) 只需考虑“之后添加字符可能构成某个 \(S_i\)” 的情况,因此可能的状态不超过 \(8 \times 10\) 种。预处理好 \(j\) 的转移,\(\mathrm{dp}\) 的复杂度就是 \(\mathcal{O}(L \times 2^N \times 80 \times 26)\)
代码实现
#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;
mint dp[101][1<<8][85];
int main() {
int n, l;
cin >> n >> l;
vector<string> S(n);
rep(i, n) cin >> S[i];
map<string, int> id;
rep(i, n) {
rep(j, S[i].size()) {
string prefix = S[i].substr(0, j+1);
id[prefix] = 0;
}
}
id[""] = 0;
int m = 0;
vector<string> sufs;
for (auto& p : id) {
p.second = m++;
sufs.push_back(p.first);
}
vector<int> mask(m);
rep(i, m) {
string t = sufs[i];
rep(j, n) {
if (t.ends_with(S[j])) mask[i] |= 1<<j;
}
}
vector to(m, vector<int>(26));
rep(j, m) {
rep(c, 26) {
string t = sufs[j]; t += 'a'+c;
while (!id.count(t)) t.erase(t.begin());
to[j][c] = id[t];
}
}
int n2 = 1<<n;
dp[0][0][0] = 1;
rep(i, l)rep(s, n2)rep(j, m) {
mint now = dp[i][s][j];
if (now == 0) continue;
rep(c, 26) {
int ni = i+1;
int nj = to[j][c];
int ns = s | mask[nj];
dp[ni][ns][nj] += now;
}
}
mint ans;
rep(j, m) ans += dp[l][n2-1][j];
cout << ans.val() << '\n';
return 0;
}
其实也可以ac自动机上dp,好像过的人基本写的都是这个,所以应该不需要讲了吧
代码实现
#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 Aho {
bool inited;
using MP = map<char, int>;
vector<MP> to;
vector<int> cnt, fail;
Aho(): to(1), cnt(1) {}
int add(const string& s) {
int v = 0;
for (char c : s) {
if (!to[v].count(c)) {
to[v][c] = to.size();
to.push_back(MP());
cnt.push_back(0);
}
v = to[v][c];
}
cnt[v]++;
return v;
}
vector<int> vs;
void init() {
fail = vector<int>(to.size(), -1);
queue<int> q;
q.push(0);
while (q.size()) {
int v = q.front(); q.pop();
vs.push_back(v);
for (auto [c, u] : to[v]) {
fail[u] = (*this)(fail[v], c);
cnt[u] += cnt[fail[u]];
q.push(u);
}
}
}
int operator()(int v, char c) const {
while (v != -1) {
auto it = to[v].find(c);
if (it != to[v].end()) return it->second;
v = fail[v];
}
return 0;
}
int operator[](int v) const { return cnt[v]; }
};
mint dp[101][85];
int main() {
int n, l;
cin >> n >> l;
vector<string> S(n);
rep(i, n) cin >> S[i];
mint ans;
rep(s, 1<<n) {
Aho aho;
rep(i, n) if (s>>i&1) aho.add(S[i]);
aho.init();
int m = aho.to.size();
rep(i, l+1)rep(j, m) dp[i][j] = 0;
dp[0][0] = 1;
rep(i, l)rep(j, m) {
mint now = dp[i][j];
if (now == 0) continue;
rep(c, 26) {
int ni = i+1;
int nj = aho(j, 'a'+c);
if (!aho[nj]) dp[ni][nj] += now;
}
}
mint now;
rep(j, m) now += dp[l][j];
ans += __builtin_parity(s) ? -now : now;
}
cout << ans.val() << '\n';
return 0;
}
G. Count Simple Paths 2
不断删除度数为 \(1\) 的点,缩合度数为 \(2\) 的点,然后直接 \(\mathrm{dfs}\) 就能过!
这题的难度主要在于建虚树的图
代码实现
#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;
vector<vector<int>> to(n);
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
to[a].push_back(b);
to[b].push_back(a);
}
vector<int> deg(n);
vector<bool> live(n, true);
{
rep(i, n) deg[i] = to[i].size();
queue<int> q;
for (int i = 1; i < n-1; ++i) if (deg[i] == 1) q.push(i);
while (q.size()) {
int v = q.front(); q.pop();
live[v] = false;
for (int u : to[v]) {
deg[u]--;
if (deg[u] == 1 and u != 0 and u != n-1) q.push(u);
}
}
}
int nn;
vector<vector<P>> g;
{
vector<int> vs;
rep(i, n) if (live[i] and (deg[i] > 2 or i == 0 or i == n-1)) vs.push_back(i);
vector<int> vid(n, -1);
nn = vs.size();
rep(i, nn) vid[vs[i]] = i;
g.resize(nn);
for (int v : vs) {
for (int u : to[v]) if (live[u]) {
int len = 1, pre = v;
while (vid[u] == -1) {
int nu = -1;
for (int w : to[u]) {
if (w == pre) continue;
if (!live[w]) continue;
nu = w;
}
pre = u;
u = nu;
len++;
}
g[vid[v]].emplace_back(vid[u], len);
}
}
}
vector<int> ans(n);
vector<bool> used(nn);
auto f = [&](auto& f, int v, int l) {
if (v == nn-1) {
ans[l]++;
return;
}
used[v] = true;
for (auto [u, len] : g[v]) {
if (used[u]) continue;
f(f, u, l+len);
}
used[v] = false;
};
f(f, 0, 0);
rep(i, n) if (i) cout << ans[i] << ' ';
return 0;
}
浙公网安备 33010602011771号