【校内模拟】树(树哈希)(状压DP)

题解:

和ZJOI2018线图里面的处理方式一样。

找到重心,以重心为根。然后对于每一个子树哈希一下。

接下来就很简单了,我们直接枚举A树里面的每一个点和B树的根匹配,然后状压DP算一下孩子的匹配方案数。

复杂度有一个显然的上界是 O ( n m 3 2 m 2 ) O(nm^32^{\frac{m}{2}}) O(nm322m),但是里面有两个 m m m 2 m 2 2^{\frac{m}{2}} 22m是远远到不了的,目前不清楚有没有更加精确的上界。


代码:

#include
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=1e9+7;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}

int n,m;

cs int N=2e3+7,M=50;
std::vector<int> G[N],E[M],H[M];
struct edge{int u,v;}eg[N];

int maxn,rt1,rt2;

inline int dfs(int u,int p){
	int s=1,mx=0;
	for(int re v:E[u])if(v!=p){
		int t=dfs(v,u);
		mx=std::max(t,mx);
		s+=t;
	}
	mx=std::max(mx,m-s);
	if(mx<maxn)maxn=mx,rt1=u,rt2=0;
	else if(mx==maxn)rt2=u;
	return s;
}

int tot,typ[M];
std::vector<int> val[M];

inline void get_hash(int u,int p){
	static std::unordered_map<ll,int> id;
	std::vector<int> t;ll res=0;
	for(int re v:H[u])if(v!=p){
		get_hash(v,u);
		t.push_back(typ[v]);
	}
	std::sort(t.begin(),t.end());
	for(int re i=0;i<t.size();++i)res=res*12345^t[i];//cerr<<"res : "<
	typ[u]=id.count(res)?id[res]:(val[++tot]=t,id[res]=tot);
}

int match[N*5][M],sol[N*5][M];
int id[N][N],cur;

inline int cal(int u,int p,int t){
	if(!id[u][p])id[u][p]=++cur;
	int idx=id[u][p];
	if(sol[idx][t])return match[idx][t]-1;
	sol[idx][t]=true;
	int up=val[t].size(),S=1<<up;
	int *f=new int[S];
	memset(f,0,sizeof(int)*S);f[0]=1;
	for(int re v:G[u])if(v!=p)
	for(int re s=S-1;~s;--s)if(f[s])
	for(int re i=0;i<up;++i)
	if(!(s&(1<<i))&&(!i||(s&(1<<i-1))||val[t][i]!=val[t][i-1]))
	Inc(f[s|(1<<i)],mul(f[s],cal(v,u,val[t][i])));
	match[idx][t]=f[S-1]+1;delete []f;
	return match[idx][t]-1;
}

int ans;
inline void solve_1(){
	for(int re u=1;u<=m;++u)
	for(int re v:E[u])H[u].push_back(v);
	get_hash(rt1,0);int t=typ[rt1];
	for(int re i=1;i<=n;++i)Inc(ans,cal(i,0,t));
}

inline void solve_2(){
	if(rt1>rt2)std::swap(rt1,rt2);
	for(int re u=1;u<=m;++u)
	for(int re v:E[u])if(u<v&&(u!=rt1||v!=rt2))
	H[u].push_back(v),H[v].push_back(u);
	++m;
	H[m].push_back(rt1);H[rt1].push_back(m);
	H[m].push_back(rt2);H[rt2].push_back(m);
	get_hash(m,0);int t1=val[typ[m]][0],t2=val[typ[m]][1];
	for(int re i=1;i<n;++i){
		int u=eg[i].u,v=eg[i].v;
		Inc(ans,mul(cal(u,v,t1),cal(v,u,t2)));
		if(t1!=t2)Inc(ans,mul(cal(v,u,t1),cal(u,v,t2)));
	}
}

signed main(){
#ifdef zxyoi
	freopen("tree.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);
#endif
#endif
	scanf("%d",&n);
	for(int re i=1;i<n;++i){
		int u,v;scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
		eg[i]=(edge){u,v};
	}
	scanf("%d",&m);
	for(int re i=1;i<m;++i){
		int u,v;scanf("%d%d",&u,&v);
		E[u].push_back(v);
		E[v].push_back(u);
	}maxn=m;dfs(1,0);
	if(rt2)solve_2();
	else solve_1();
	cout<<ans<<"\n";
	return 0;
} 

你可能感兴趣的:(校内模拟,状压DP,树形DP)