Loading

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;
}
posted @ 2025-10-21 21:41  HaHeHyt  阅读(30)  评论(0)    收藏  举报