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;
}

  

posted @ 2020-07-29 22:04  阿波罗2003  阅读(481)  评论(0编辑  收藏  举报