CodeForces 923F. Public Service

链接:

link

题意:

给两棵树,求一个排列 p p ,使得不存在 (i,j) 在第一棵树中有边且 (pi,pj) ( p i , p j ) 在第二棵树中有边。

题解:

不难发现如果某棵树是个菊花图就无解,不然就有解。

考虑化为子问题处理,分为三种情况:

如果一棵树删去一个点之后是一个菊花图,那么找到那个点 v v 以及跟它相连的 u 和跟 u u 相连的 w ,那么在第二棵树中,找到一个叶子 w w ′ 和跟它相连的 v v ′ ,并暴力寻找跟 v v ′ w w ′ 都不相连的点 u u ′ ,由于第二棵树不是菊花图,所以 u u ′ 一定存在,然后其他点随意映射就好了。

如果 n5 n ≤ 5 ,暴力枚举所有映射判断。

否则从两棵树找出两对叶子,满足删去它们之后不会变成菊花图,然后它们进行映射,变成子问题递归处理。

代码:

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 100005;

struct Graph {
  set <pii> all, edg, lef;
  set <int> ver, adj[N];
  int n, d[N];

  inline void AddEdge(int x, int y) {
    edg.insert(mp(x, y)), edg.insert(mp(y, x));
    adj[x].insert(y), adj[y].insert(x);
    ++d[x], ++d[y];
  }

  inline void Init() {
    for (int i = 1; i <= n; ++i) {
      ver.insert(i), all.insert(mp(d[i], i));
      if (d[i] == 1) {
        lef.insert(mp(*adj[i].begin(), i));
      }
    }
  }

  inline int Star() {
    return ver.size() - all.rbegin()->X - 1;
  }

  inline void Erase(int x) {
    int y = *adj[x].begin();
    ver.erase(x), lef.erase(mp(y, x)), all.erase(mp(1, x));
    adj[x].erase(y), adj[y].erase(x);
    all.erase(mp(d[y], y)), all.insert(mp(--d[y], y));
    if (d[y] == 1) {
      lef.insert(mp(*adj[y].begin(), y));
    }
  }

  inline pii Leaves() {
    set <pii> s;
    s.insert(*lef.begin()), s.insert(*++lef.begin());
    s.insert(*lef.rbegin()), s.insert(*++lef.rbegin());
    int big = -1;
    for (auto a : s) {
      if (d[a.X] > 2) {
        big = a.X;
        break;
      }
    }
    if (~big) {
      int x = -1, y = -1;
      for (auto a : s) {
        if (a.X == big) {
          x = a.Y;
        } else {
          y = a.Y;
        }
      }
      return mp(x, y);
    } else {
      return mp(lef.begin()->Y, lef.rbegin()->Y);
    }
  }
} G, H;

int n, ans[N], ret[N];

inline void SolveStar(Graph &G, Graph &H) {
  int u, v, w, uu, vv, ww;
  vector <int> g, h;
  u = (++G.all.rbegin())->Y, v = *G.adj[u].begin(), w = *G.adj[u].rbegin();
  if (G.d[w] == 1) {
    swap(v, w);
  }
  ww = H.lef.begin()->Y, vv = H.lef.begin()->X, uu = -1;
  for (auto x : H.ver) {
    if (H.edg.find(mp(x, vv)) == H.edg.end() && H.edg.find(mp(x, ww)) == H.edg.end()) {
      uu = x;
      break;
    }
  }
  ans[u] = uu, ans[v] = vv, ans[w] = ww;
  for (auto x : G.ver) {
    if (x != u && x != v && x != w) {
      g.pb(x);
    }
  }
  for (auto x : H.ver) {
    if (x != uu && x != vv && x != ww) {
      h.pb(x);
    }
  }
  for (int i = 0; i < g.size(); ++i) {
    ans[g[i]] = h[i];
  }
}

inline void SolveBrute() {
  vector <int> g, h;
  int n = G.ver.size();
  for (auto x : G.ver) {
    g.pb(x);
  }
  for (auto x : H.ver) {
    h.pb(x);
  }
  do {
    bool flg = true;
    for (int i = 0; i < n; ++i) {
      for (int j = 0; j < i; ++j) {
        if (G.edg.find(mp(g[i], g[j])) != G.edg.end() && H.edg.find(mp(h[i], h[j])) != H.edg.end()) {
          flg = false, i = j = n;
        }
      }
    }
    if (flg) {
      for (int i = 0; i < n; ++i) {
        ans[g[i]] = h[i];
      }
      return ;
    }
  } while (next_permutation(g.begin(), g.end()));
  assert(false);
}

inline void Solve() {
  if (G.ver.size() <= 5) {
    SolveBrute();
  } else if (G.Star() == 1) {
    SolveStar(G, H);
  } else if (H.Star() == 1) {
    SolveStar(H, G);
    for (int i = 1; i <= n; ++i) {
      ret[ans[i]] = i;
    }
    for (int i = 1; i <= n; ++i) {
      ans[i] = ret[i];
    }
  } else {
    pii a = G.Leaves(), b = mp(*G.adj[a.X].begin(), *G.adj[a.Y].begin()), c = H.Leaves(), d = mp(*H.adj[c.X].begin(), *H.adj[c.Y].begin());
    G.Erase(a.X), G.Erase(a.Y), H.Erase(c.X), H.Erase(c.Y), Solve();
    if (ans[b.X] != d.X && ans[b.Y] != d.Y) {
      ans[a.X] = c.X, ans[a.Y] = c.Y;
    } else {
      ans[a.X] = c.Y, ans[a.Y] = c.X;
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), G.n = H.n = n;
  for (int i = 1, x, y; i < n; ++i) {
    Read(x), Read(y), G.AddEdge(x, y);
  }
  for (int i = 1, x, y; i < n; ++i) {
    Read(x), Read(y), H.AddEdge(x - n, y - n);
  }
  G.Init(), H.Init();
  if (!G.Star() || !H.Star()) {
    puts("No");
    return 0;
  }
  puts("Yes");
  Solve();
  for (int i = 1; i <= n; ++i) {
    printf("%d%c", ans[i] + n, i == n ? '\n' : ' ');
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值