Codeforces 1383C String Transformation 2 - 图论 - 动态规划
题目传送门
我怎么菜到这种比赛也能下分。
感觉除了 C,这场比赛剩下的题目都有点愚蠢,就懒得写题解了。注意 D 是要求的是集合相同不是数组相同。
考虑如果 $s_i \neq t_i$ 那么在 $G$ 中连一条 $s_i \rightarrow t_i$ 的有向边。题目相当于要求在另一个初始没有边的新图 $G'$ 中添加若干边,满足每个点经过时间递增的边能够到达旧图中与它直接相连的点。
显然每个弱连通块独立。所以假设 $G'$ 弱连通,设图 $G'$ 的点数为 $n$,其中最大的点导出 DAG 的大小为 $m$,那么答案为 $2n - 1 - m$。
先证明这个是上界,假设不在 DAG 中为 $m + 1, \cdots, n$,那么依次操作 $(m + 1, m + 2), (m + 2, m + 3), \cdots, (n - 1, n), (n, m + 1), \cdots, (m + 1, m + 2), \cdots, (n - 2,n - 1)$,显然此时每个点经过时间递增的边能够到达这中间的所有点,假设 DAG 的拓扑序为 $1, \cdots, m$,那么依次操作 $(n - 1, 1), (1, 2), \cdots, (m - 1 ,m)$。
然后证明这是下界,考虑依次进行每个操作,考虑每个弱连通块维护它的最大点导出 DAG。显然这个 DAG 中的点在图 $G$ 中也是一个点导出 DAG。考虑如果操作了 $(u, v)$
- 如果 $u, v$ 在同一弱连通块内,考虑如果 $v$ 在 DAG 中,那么删掉 $v$,因此点导出的 DAG 之和至多减少 1。
- 如果在不同弱连通块,那么连接它们的 DAG。
假设操作了 $k$ 次,那么第二种情况会恰好发生 $n - 1$ 次,情况一至多发生 $k - n + 1$ 次,所以有 $|DAG| \geqslant 2n - k - 1$,即 $k \geqslant 2n - |DAG| - 1$。当 $|DAG|$ 取到最大值时,即为 $k$ 的下界。
剩下是一个普及组 dp,相信大家都会。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
template <typename T>
boolean vmin(T& a, T b) {
return (a > b) ? (a = b, true) : (false);
}
template <typename T>
boolean vmax(T& a, T b) {
return (a < b) ? (a = b, true) : (false);
}
template <typename T>
T smax(T x) {
return x;
}
template <typename T, typename ...K>
T smax(T a, const K &...args) {
return max(a, smax(args...));
}
template <typename T>
T smin(T x) {
return x;
}
template <typename T, typename ...K>
T smin(T a, const K &...args) {
return min(a, smin(args...));
}
// debugging lib
#define VN(x) #x
#define Vmsg(x) VN(x) << " = " << (x)
#define printv(x) cerr << VN(x) << " = " << (x);
#define debug(...) fprintf(stderr, __VA_ARGS__);
template <typename A, typename B>
ostream& operator << (ostream& os, const pair<A, B>& z) {
os << "(" << z.first << ", " << z.second << ')';
return os;
}
template <typename T>
ostream& operator << (ostream& os, const vector<T>& a) {
boolean isfirst = true;
os << "{";
for (auto z : a) {
if (!isfirst) {
os << ", ";
}
os << z;
isfirst = false;
}
os << '}';
return os;
}
#define pii pair<int, int>
#define pil pair<int, ll>
#define pli pair<ll, int>
#define ll long long
#define ull unsigned long long
const int inf = (signed) (~0u >> 2);
const ll llf = (signed ll) (~0ull >> 2);
#define pb push_back
#define eb emplace_back
#define fi first
#define sc second
template <typename T>
int vsize(vector<T>& x) {
return (signed) x.size();
}
template <typename T>
int discrete(T* a, int* b, int n) {
vector<T> v(a + 1, a + n + 1);
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
return v.size();
}
mt19937 rng (time(NULL));
int randint(int l, int r) {
return rng() % (r - l + 1) + l;
}
const int N = 1e5 + 5;
int T, n;
char s[N], t[N];
int G0[22], G[22];
int uf[22];
int find(int x) {
return uf[x] == x ? x : (uf[x] = find(uf[x]));
}
void unit(int x, int y) {
x = find(x);
y = find(y);
if (x ^ y) {
uf[x] = y;
}
}
int solve(vector<int> p) {
memset(G, 0, sizeof(G));
int n = p.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if ((G0[p[i]] >> p[j]) & 1) {
G[i] |= (1 << j);
}
}
}
vector<bool> dp (1 << n, 0);
dp[0] = true;
int res = 0;
for (int s = 0; s < (1 << n); s++) {
if (!dp[s]) continue;
res = max(res, __builtin_popcount(s));
for (int i = 0; i < n; i++) {
if (!((s >> i) & 1) && !(G[i] & s)) {
dp[s | (1 << i)] = true;
}
}
}
return 2 * n - 1 - res;
}
void solve() {
cin >> n;
string s, t;
cin >> s;
cin >> t;
memset(G0, 0, sizeof(G0));
for (int i = 0; i < 20; i++) {
uf[i] = i;
}
for (int i = 0; i < n; i++) {
if (s[i] != t[i]) {
G0[s[i] - 'a'] |= 1 << (t[i] - 'a');
unit(s[i] - 'a', t[i] - 'a');
}
}
int ans = 0;
for (int i = 0; i < 20; i++) {
if (find(i) == i) {
vector<int> p;
for (int j = 0; j < 20; j++) {
if (find(j) == i) {
p.push_back(j);
}
}
ans += solve(p);
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> T;
while (T--) {
solve();
}
return 0;
}
浙公网安备 33010602011771号