当前位置: 首页 > news >正文

Ancestral Problem 题解

匈牙利吊打 dinic

下面默认二分图匹配的复杂度是 dinic\(\mathcal{O}(m\sqrt n)\),其中 \(n\) 是点数,\(m\) 是边数。

暂时默认 \(m=\mathcal{O}(n)\) 因为不影响分析复杂度。


首先容易写出 \(\mathcal{O}(n^{3.5})\)dp

钦定第二棵树以 \(1\) 为根,枚举第一棵树的根,然后记 \(f_{x,i}\) 表示第一棵树的 \(x\) 子树能否和第二棵的 \(i\) 子树匹配。

转移就把 \(x,i\) 的所有儿子拎出来,通过之前 dp 值把能匹配的连边,然后判断二分图是否存在完美匹配。


你思考第一棵树本质不同的子树状态只有 \(\mathcal{O}(n)\) 个并非 \(\mathcal{O}(n^2)\) 个。

这时你直接记忆化 \(f_{(x,fa),y}\) 表示第一棵树的以 \(fa\) 为根的 \(x\) 子树能否和第二棵的 \(y\) 子树匹配。

递归下去也是记忆化。

这样就 \(\mathcal{O}(n^{2.5})\) 了吗?并非,因为你还要枚举儿子。

直接来两个菊花就给你卡掉了。


考虑换根 dp,记 \(f_{x,i}\) 表示以 \(1\) 为根,第一棵树的 \(x\) 子树能否和第二棵的 \(i\) 子树匹配。这个是容易求的。

然后换根记 \(g_{x,i}\) 表示以 \(x\) 根,第一棵树的 \(fa_x\) 子树能否和第二棵的 \(i\) 子树匹配。

考虑已知 \(f\),已知 \(g_{x,*}\) 如何转移得到 \(g_{y,*}\),其中 \(y\)\(x\) 的儿子。

根据已知,我们能知道以 \(x\) 为根的时候,\(x\) 的邻域和第二棵树能否匹配。其中儿子 \(u\) 通过 \(f_u\) 得到,父亲通过 \(g_{x}\) 得到。

然后我们直接和某个点 \(i\) 匹配。此时若某个儿子 \(y\)\(x\)\(i\) 匹配的必经点,则 \(g_{y,i}=0\),否则 \(g_{y,i}=1\)

二分图匹配必经点

  • 结论是:非必经点 左部是 \(S\) 走残量网络中边权为 \(1\) 的边能走到的所有左部点,右部是 \(T\) 走残量网络中边权为 \(0\) 的边能走到的所有右部点。

于是所有复杂度都对了,复杂度 \(\mathcal{O}(nm\sqrt n)\),理论根本过不去,但是 dinic 小常数跑不满导致能过。


卡时卡空间注意几点:

  • 图的边要开到 \(nm+n+m\),否则可能被卡。

  • 前向星存图的几个数组分开放。

  • dp 数组 \(f,g\),以及图的边权都用 \(2\times 10^7\)bitset 存储。

  • 可以把 dfs 序存下来做后面的 dfs

  • 进行必要的剪枝。

于是你写出了如下代码,足以通过:

#include<bits/stdc++.h>
#define LL long long
#define u64 unsigned long long
#define sz(x) ((int)x.size())
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
inline void rd(int &x){x=0;char c=getchar();while(!isdigit(c)) c=getchar();while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
const int N=2e5+5,M=2e7+2e5+5;
int n,m,S,T,fa[N];bool ok,v[N];
bitset<M>f,g;
basic_string<int>G1[N],G2[N],tn;
namespace FL
{int hd[N],_hd[N],tt,d[N],to[M],nx[M];bitset<M>W;inline void add(int u,int v,int w){to[++tt]=v,nx[tt]=hd[u],W[tt]=w,hd[u]=tt;to[++tt]=u,nx[tt]=hd[v],W[tt]=0,hd[v]=tt;}inline void cl(){fill_n(hd,T+1,0);tt=1;}inline bool bfs(){fill_n(d,T+1,0);d[S]=1;queue<int>q;q.push(S);while(!q.empty()){int t=q.front();q.pop();for(int i=hd[t];i;i=nx[i]){int y=to[i];if(!d[y]&&W[i]) d[y]=d[t]+1,q.push(y);}}return d[T];}int dfs(int x,int F){if(x==T) return F;int o=F;for(int &i=_hd[x];i;i=nx[i]){int y=to[i];if(d[y]==d[x]+1&&W[i]){int t=dfs(y,min(o,(int)W[i]));if(t) o--,W[i].flip(),W[i^1].flip();if(!o) break;}}return (o==F)&&(d[x]=0),F-o;}inline int dinic(){int s=0;while(bfs()) memcpy(_hd,hd,(T+1)<<2),s+=dfs(S,1e9);return s;}void DFS(int x){v[x]=1;for(int i=hd[x];i;i=nx[i]){int y=to[i];if(W[i]&&!v[y]) DFS(y); }}inline void get(){fill_n(v,T+1,0),DFS(S);}
}
void I(int x,int F)
{if(F) G2[x].erase(find(G2[x].begin(),G2[x].end(),F));for(int y:G2[x]) I(y,x);
}
inline int W(int i,int j){return (i-1)*m+j;}
void dfs(int x,int F)
{tn+=x;fa[x]=F;if(sz(G1[x])==1&&G1[x][0]==F){for(int i=1;i<=m;i++) if(!sz(G2[i])) f.set(W(x,i));return;}basic_string<int>g;for(int y:G1[x]) if(y^F) dfs(y,x),g+=y;for(int y=1;y<=m;y++){int A=sz(g),B=sz(G2[y]);if(A<B) continue;FL::cl();S=0;T=A+B+1;for(int i=1;i<=A;i++) FL::add(S,i,1);for(int i=1;i<=B;i++) FL::add(i+A,T,1);for(int i=0;i<A;i++) for(int j=0;j<B;j++)if(f[W(g[i],G2[y][j])]) FL::add(i+1,j+A+1,1);if(FL::dinic()^B) continue;if(y==1) return ok=1,void();f.set(W(x,y));}if(ok) return;
}
int main()
{int _;rd(_);while(_--){for(int i=1;i<=n;i++) G1[i].clear();for(int i=1;i<=m;i++) G2[i].clear();rd(n);for(int i=1,u,v;i<n;i++) rd(u),rd(v),G1[u]+=v,G1[v]+=u;rd(m);for(int i=1,u,v;i<m;i++) rd(u),rd(v),G2[u]+=v,G2[v]+=u;for(int i=0;i<=n*m;i++) f[i]=g[i]=0;if(m>n){puts("NO");continue;}if(m==1){puts("YES");continue;}ok=0;I(1,0);tn.clear();dfs(1,0);if(ok){puts("YES");continue;}for(int x:tn){int F=fa[x];for(int y=1;y<=m;y++){int A=sz(G1[x]),B=sz(G2[y]);if(A<B) continue;FL::cl();S=0;T=A+B+1;for(int i=1;i<=A;i++) FL::add(S,i,1);for(int i=1;i<=B;i++) FL::add(i+A,T,1);for(int i=0;i<A;i++) for(int j=0;j<B;j++){int X=G1[x][i],Y=G2[y][j];if(X==F?g[W(x,Y)]:f[W(X,Y)]) FL::add(i+1,j+A+1,1);}if(FL::dinic()^B) continue;if(y==1){ok=1;break;}FL::get();for(int i=0;i<A;i++) if(v[i+1]&&G1[x][i]!=F) g.set(W(G1[x][i],y));}if(ok) break;}puts(ok?"YES":"NO");}return 0;
}
http://www.hskmm.com/?act=detail&tid=36101

相关文章:

  • AWS IAM角色最佳实践:构建云安全的核心防线
  • 正睿 2025 NOIP 20连测 Day6
  • Hetao P5593 删 题解 [ 蓝 ] [ 线性 DP ] [ DFS 序 ] [ 虚树 ]
  • o(N^2)找出所有回文子串
  • 第二次高级程序作业
  • 大学生需要认真听课的肌肉记忆(注意力训练)
  • 初始人工智能和机器学习
  • 10/21
  • 蛋白表达技术概述
  • 二叉树的中序遍历- 递归原理 - MKT
  • 友链测试
  • 二叉树的中序遍历- 二叉树基本-递归 - MKT
  • 做了一个概率小游戏,没想到服务器被打爆被攻击了!原因竟然是他?真没想到...
  • 接下来的目标
  • 阿里云对象存储OSS之Java - Soul
  • 敬启,致那时的我
  • 后量子密码学技术与标准化进程解析
  • 10月21日
  • 清楚标签默认样式,内容溢出盒子时的处理
  • 用 大模型 和 Gradio 构建一个 AI 反向词典
  • MySQL 事务
  • python概念详解
  • JAVA基础理解
  • 1279. 红绿灯路口
  • 软件工程第三次作业
  • 用户消费行为数据分析(随笔)
  • linux常用命令总结
  • sqlserver 主要的日期函数及用法示例
  • ICPC2022沈阳 游记(VP)
  • 大数据分析基础及应用案例:第四周学习报告——线性回归模型