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

浙公网安备 33010602011771号