题解 P6084 【[JSOI2015]isomorphism】

Great_Influence

2020-02-11 22:31:26

Solution

这道题可以分成两部分。 1. 缩树。 2. 树同构。 第一步直接在原树上重新建树即可,关键在于第二步。 这里的主要问题在于对无根树判同构很麻烦,因此我们可以考虑直接将无根树转换成有根树。 至于选什么点做根, 这个就很明显了:重心。 唯一的问题在于一棵树的重心可能有两个。不过这两个重心肯定是直接相连的,我们把相连的边断开看做两棵树即可。 然后跑你最喜欢的有根树哈希算法就可以了。我这里选择的哈希值计算方法是: $$Hash[u]=\oplus(Hash[v]-W[u])*W[v]$$ 其中 $W[u]$ 是随机出来的某种系数。 代码: ```cpp #include<bits/stdc++.h> #define Rep(i,a,b) for(register int i=(a);i<=(b);++i) #define Repe(i,a,b) for(register int i=(a);i>=(b);--i) #define rep(i,a,b) for(register int i=(a);i<(b);++i) #define pb push_back #define mp make_pair using namespace std; inline void Chkmax(int&u,int v){u<v?u=v:0;} const int MAXN = 1e4 + 7; vector <int> ed[MAXN]; set < pair<int, unsigned long long> > G; static int rt1, rt2, d[MAXN]; static int sz[MAXN], F[MAXN]; static unsigned long long w[MAXN]; void getcentr(int u, int fa, int al) { sz[u] = 1, F[u] = 0; for(int v : ed[u]) if(v ^ fa) getcentr(v, u, al), Chkmax(F[u], sz[v]), sz[u] += sz[v]; Chkmax(F[u], al - sz[u]); if(!rt1 || F[u] < F[rt1]) rt1 = u, rt2 = fa; } static unsigned long long hs[MAXN]; void geths(int u, int fa) { sz[u] = 1; hs[u] = 0; for(int v : ed[u]) if(v ^ fa) geths(v, u), sz[u] += sz[v]; for(int v : ed[u]) if(v ^ fa) hs[u] ^= (hs[v] - w[sz[u]]) * w[sz[v]]; hs[u] ^= w[sz[u]]; } inline unsigned long long calc(int n, int zz) { rt1 = rt2 = 0; Rep(i, 1, n) if(d[i] == 1) {getcentr(i, 0, zz); break;} //cerr << rt1 << ' ' << rt2 <<endl; if(F[rt1] == F[rt2]) { geths(rt1, rt2), geths(rt2, rt1); return hs[rt1] ^ hs[rt2]; } geths(rt1, 0); return hs[rt1]; } static vector <int> us[MAXN]; static pair <int, int> cn[MAXN]; void shrink(int u, int fa, int tf, int & n) { if(d[u] == 2) {for(int v : us[u]) if(v ^ fa) shrink(v, u, tf, -- n);} else { if(tf) ed[u].pb(tf), ed[tf].pb(u); for(int v : us[u]) if(v ^ fa) shrink(v, u, u, n); } } #define read(x) cin>>x inline void init() { static int n, m, u, v; srand(time(NULL)); Rep(i, 1, 10000) w[i] = rand() | (unsigned long long)rand() << 30; read(m); Rep(i, 1, m) { read(n); Rep(i, 1, n) us[i].resize(0), ed[i].resize(0), d[i] = 0; Rep(i, 1, n - 1) read(u), read(v), ++ d[u], ++ d[v], us[u].pb(v), us[v].pb(u); int s = n; Rep(i, 1, n) if(d[i] == 1) {shrink(i, 0, 0, s); break;} G.insert(mp(s, calc(n, s))); } } inline void solve() { cout << G.size() << endl; for(auto z : G) printf("%d ", z.first); puts(""); } int main() { init(); solve(); return 0; } ```