2020牛客暑期多校训练营(第十场)J.Identical Trees

题目链接
我们从两个树的根开始递归下去,设 d p [ l ] [ r ] dp[l][r] dp[l][r]为T1的l为根的子树,和T2的r为根的子树的最小代价。
然后往上合并的时候相当于一个二分图最小匹配。

先用树hash判同构,然后用最小费用最大流找到最小代价即可。

ps:比赛的时候,一直再递归函数里面开vector,结果爆炸一直T,,,长记性。。。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include 
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pll pair
#define pb push_back
#define wzh(x) cerr<<#x<<'='<
const int MAX_N = 1005;
const int MAX_M = 50000;


int pr[N], ar[N], _cnt;
void PP() {
  for (int i = 2; i < N; i++) {
    if (!pr[i])ar[++_cnt] = i;
    for (int j = 1; j <= _cnt && 1ll * ar[j]*i < N; j++) {
      pr[ar[j]*i] = 1;
      if (i % ar[j] == 0)break;
    }
  }
}

struct edge {
  int v, c, w, next;  // v 表示边的另一个顶点,c 表示当前剩余容量,w 表示单位流量费用
} ;
struct MCMF
{
  edge e[MAX_M];
  int p[MAX_N], s, t, eid;  // s 表示源点,t 表示汇点,需要在进行 costflow 之前设置完毕
  int n;
  void init(int _n) {
    n = _n;
    s = 1;
    t = _n;
    for (int i = 0; i <= n; i++)p[i] = -1;
    eid = 0;
  }
  void insert(int u, int v, int c, int w) {
    e[eid].v = v;
    e[eid].c = c;
    e[eid].w = w;
    e[eid].next = p[u];
    p[u] = eid++;
  }
  void addedge(int u, int v, int c, int w) {
    insert(u, v, c, w);
    insert(v, u, 0, -w);
  }
  bool inq[MAX_N];
  int d[MAX_N];  // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
  int pre[MAX_N];  // 最短路中连向当前顶点的边的编号
  bool spfa() {  // 以源点 s 为起点计算单源最短路,如果不存在从 s 到 t 的路径则返回 false,否则返回 true
    for (int i = 0; i <= n; i++)d[i] = 1e9, inq[i] = 0, pre[i] = -1;
    d[s] = 0;
    inq[s] = true;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      inq[u] = false;
      for (int i = p[u]; i != -1; i = e[i].next) {
        if (e[i].c) {    //注意这个条件!!!spfa这里是以w求最短路的,但仍然不能忽略容量c的考虑!
          int v = e[i].v;
          if (d[u] + e[i].w < d[v]) {
            d[v] = d[u] + e[i].w;
            pre[v] = i;
            if (!inq[v]) {
              q.push(v);
              inq[v] = true;
            }
          }
        }
      }
    }
    return pre[t] != -1;
  }

  int min_cost_flow() {  // 计算最小费用最大流
    int ret = 0;  // 累加和
    while (spfa()) {
      int flow = 555;
      for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
        flow = min(e[pre[i]].c, flow);  // 计算当前增广路上的最小流量
      }
      for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
        e[pre[i]].c -= flow;     //容量是一定要跟着变化的,毕竟要继续循环使用spfa来更新下一条“能走的(看容量)”最短路。
        e[pre[i] ^ 1].c += flow;
        ret += e[pre[i]].w * flow;
      }
    }
    return ret;
  }
} Ga;
int n, a[N], b[N], RA, RB;
vector<int>v[N], g[N];
int sz1[N],sz2[N];
const int inf=n;
unsigned long long H1[N], H2[N];
int al[555][555];
void gao(int x, int y) {
  if(sz1[x]!=sz2[y])return ;
  if(sz1[x]==1){
    al[x][y]=(x!=y);
    return;
  }
  for(auto l:v[x]){
    for(auto r:g[y]){
      gao(l,r);
    }
  }
  Ga.init(2+v[x].size()+g[y].size());
  int sum=2+v[x].size()+g[y].size();
  for(int i=0;i<v[x].size();i++){
    Ga.addedge(1,i+2,1,0);
    for(int j=0;j<g[y].size();j++){
      int dx=v[x][i];
      int dy=g[y][j];
      //if(al[dx][dy]!=inf){
      Ga.addedge(i+2,j+1+v[x].size()+1,1,al[dx][dy]);
    }
  }
  for(int i=0;i<g[y].size();i++){
    Ga.addedge(i+1+v[x].size()+1,sum,1,0);
  }
  al[x][y] = min(Ga.min_cost_flow()+(x!=y),al[x][y]);
  return ;
}
void check1(int x) {
  sz1[x] = 1; H1[x] = 1;
  if (!v[x].size()) {
    H1[x] = 1;
    return;
  }
  H1[x] = 0;
  for (auto k : v[x]) {
    check1(k);
    sz1[x] += sz1[k];
  }
  for (auto k : v[x]) {
    H1[x] += H1[k] * ar[sz1[k]];
  }
  H1[x] *= sz1[x];
}
void check2(int x) {
  sz2[x] = 1; H2[x] = 1;
  if (!g[x].size()) {
    H2[x] = 1;
    return;
  }
  H2[x] = 0;
  for (auto k : g[x]) {
    check2(k);
    sz2[x] += sz2[k];
  }
  for (auto k : g[x]) {
    H2[x] += H2[k] * ar[sz2[k]];
  }
  H2[x] *= sz2[x];
}
int main() {
  ios::sync_with_stdio(false);
  cin >> n; PP();
  for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      al[i][j]=555;
    }
  }
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    if (a[i])v[a[i]].pb(i);
    else RA = i;
  }
  for (int i = 1; i <= n; i++) {
    cin >> b[i];
    if (b[i])g[b[i]].pb(i);
    else RB = i;
  }
  check1(RA);
  check2(RB);
  gao(RA, RB);
  cout<<al[RA][RB]<<'\n';
  //puts("ok");
  return 0;
}
/*
6
2 3 0 2 6 3
2 3 0 2 3 5
 */

你可能感兴趣的:(二分图,hash)