FZOJ195. 「2019冬令营提高组」树(树的同构,树hash,状压dp,动态规划)

题目大意:
给出一颗 n n n 个节点的树 A A A 和一颗 m m m 个节点的树 B B B ,求 A A A 有多少个不同的连通子图与 B B B 同构,答案对 1 0 9 + 7 10^9+7 109+7 取模

我们定义两个图同构当且仅当存在一个节点的对应方案使得每个图中的每个节点恰好与另一个图中的某个节点相对应,且如果在一个图中两个节点之间有连边,它们在另一个图中对应的两个节点之间也有连边。
n ≤ 2000 , m ≤ 12 n\le 2000,m\le12 n2000,m12


注意到 m m m 很小
可以考虑暴力对应
考虑一个暴力的 d p dp dp f [ i ] [ j ] f[i][j] f[i][j] 表示 A A A 树的 i i i 点对应到 B B B 树的 j j j 子树合法的方案数
为了优化复杂度我们选取 B B B 的根的时候最好保证最深的点最浅(好像没屁用)

B B B 树上的点从下往上 d p dp dp
为了转移我们发现我们要增加一维 f [ u ] [ v ] [ k ] f[u][v][k] f[u][v][k] u ∈ A u\in A uA 对应 k ∈ B k\in B kB 同时保证以 u u u 为根的情况下不选 v v v
好像复杂度直接多了一个 n n n 的样子?
我们先考虑假设多了一个 n n n 的复杂度
转移的时候需要对 k k k 的子树情况做一个状压统计方案数

上界是 O ( n 2 m 2 2 m ) O(n^2m^22^m) O(n2m22m)
显然远远跑不满不过也过不去满分
考虑优化

我们先不考虑 v v v 的限制,先跑一边状压,再增加 v v v 这个点的限制

我们发现只要做一个类似退背包的东西

注意我们要类似退背包,那么做到 s s s 的时候,所有的 t &lt; s t&lt;s t<s 都应该把 v v v “退干净”了

也就是说要注意枚举熟顺序。不然会 w a wa wa,考场我就这么 G G GG GG
复杂度 O ( n m 2 2 m ) O(nm^22^m) O(nm22m) 远远跑不满,加上一些记忆化搜索等优化会更快

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define SZ(x) ((int)(x.size()))
#define ll long long
#define fr first
#define se second
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I &x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	//no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
#define hash Hash

const int p = 1e9+7;
const ll P = 1ll*p*p;
const int base = 31;
int to[15],tot;
int n,m;
int tmp[20];
int f[2100][2100][15];
int g[5010],t[5010],sz[15];
int cnt,fac[15];
bool ok;
ll hash[15];
inline int calc(int a,int b){return (a+b)%p;}
inline int del(int a,int b){return (a-b+p)%p;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline ll Calc(int a,int b){return (a+b)%P;}
inline ll Mul(int a,int b){return 1ll*(a%p)*(b%p)%P;}
inline int ksm(int a,int x){int now = 1;for(;x;x>>=1,a=1ll*a*a%p) if(x&1) now = 1ll*now*a%p;return now;}
struct{vector<int>G[2100];int dep[2100];}A,B;
ll dfs_B(int x,int fa){
	B.dep[x] = B.dep[fa] + 1;
	vector<ll>h;h.clear();
	rep(i,0,SZ(B.G[x])-1) if(B.G[x][i] != fa)
	    h.pb(dfs_B(B.G[x][i],x)),sz[x] += sz[B.G[x][i]];
    sort(h.begin(),h.end());
    if(ok){
        int i = 0,j;
        while(i < SZ(h)){
    	    for(j = i;j < SZ(h) && h[j] == h[i];j++);
    	    cnt = mul(cnt,fac[j-i]);i = j;
        }
    }
	ll now = 1,tmp = base;
	rep(i,0,SZ(h)-1) now = Mul(now,Mul(h[i],tmp)),tmp = Mul(tmp,base);
	sz[x]++;now = Mul(now,sz[x]);
	return now;
}
bool mycmp(int a,int b){return B.dep[a] > B.dep[b];}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	cnt = 1;
	fac[0] = 1;rep(i,1,12) fac[i] = 1ll*fac[i-1]*i%p;
	gi(n);rep(i,1,n-1){int x,y;gi(x);gi(y);A.G[x].pb(y);A.G[y].pb(x);}
	gi(m);rep(i,1,m-1){int x,y;gi(x);gi(y);B.G[x].pb(y);B.G[y].pb(x);}
	int mn = m+1,rt_b = 0;
	rep(i,1,m){
		file(sz);hash[i] = dfs_B(i,0);int mx = 0;rep(j,1,m) mx = max(mx,B.dep[j]);
		if(mx < mn) mn = mx,rt_b = i;
	}
	ok = true;file(sz);
	dfs_B(rt_b,0);
    rep(i,1,m) tmp[i] = i;
    sort(tmp+1,tmp+m+1,mycmp);
    int ans = 0;
    rep(tt,1,m){
    	int x = tmp[tt]; tot = 0;
    	rep(i,0,SZ(B.G[x])-1) if(B.dep[B.G[x][i]] == B.dep[x] + 1) to[++tot] = B.G[x][i];
    	if(tot == 0) {rep(u,1,n) rep(i,0,SZ(A.G[u])-1) f[u][A.G[u][i]][x] = 1;continue;}
    	rep(u,1,n){
    		rep(i,0,(1<<tot)-1) g[i] = 0;g[0] = 1;
    		rep(i,0,SZ(A.G[u])-1){
    			int v = A.G[u][i];
    			rep(s,0,(1<<tot)-1) t[s] = g[s];
    			rep(j,1,tot) if(f[v][u][to[j]])
    			    rep(s,0,(1<<tot)-2) if(!(s>>(j-1)&1)) 
    			        t[s+(1<<(j-1))] = calc(t[s+(1<<(j-1))],mul(g[s],f[v][u][to[j]]));
    			rep(s,0,(1<<tot)-1) g[s] = t[s];
    		}
    		if(tt == m) ans = calc(ans,g[(1<<tot)-1]);
    		rep(i,0,SZ(A.G[u])-1){
    			int v = A.G[u][i];
    			rep(j,0,(1<<tot)-1) t[j] = g[j];
    			rep(s,0,(1<<tot)-1) rep(j,1,tot)
    			    if(f[v][u][to[j]] && (s>>(j-1)&1)) 
					    t[s] = del(t[s],mul(t[s-(1<<(j-1))],f[v][u][to[j]]));
    			f[u][v][x] = t[(1<<tot)-1];
    		}
    	}
    }
    int tmp = 0;
    rep(i,1,m) if(hash[i] == hash[rt_b]) tmp++;
    cnt = mul(cnt,tmp);
    printf("%d\n",mul(ans,ksm(cnt,p-2)));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值