kmjp's blog

競技プログラミング参加記です

HackerRank HourRank 21 : C. Tree Isomorphism

最近これ系多い。
https://www.hackerrank.com/contests/hourrank-21/challenges/tree-isomorphism

問題

N頂点の木が与えられる。部分木についてisomorphicなものを重複カウントしない場合、いくつあるか。

解法

部分木の頂点を(2^N)通り総当たりし、そのうち連結なものを処理しよう。
isomorphicをまともに判定するのは無謀なので、ハッシュ値を取ってユニークなものを求めよう。
同一性判定の方法によるけどO(N*2^N*logN)程度計算量があってちょっと重いので、ハッシュ計算は適宜メモ化しておく。

int N;
vector<int> E[20];
int A[20],B[20];
int AS[20],BS[20];
vector<ll> S[20];
ll memo[19][1<<19];
int tmask[20][20];
int tmask2[20][20];

ll tree_normalize(vector<ll> T) {
	static ll momo[2]={1000000007,1000000009};
	static vector<ll> prim = {79,73,47,7,41,13,17,19,23,29,31,37,43,5,53,59,61,67,71};
	
	sort(ALL(T));
	int id=0,id2=1;
	ll a=1,b=1;
	FORR(r,T) {
		ll h=r>>32, l=r-(h<<32);
		(a+=h*prim[(id++)%prim.size()])%=momo[0];
		(b+=l*prim[(id2++)%prim.size()])%=momo[1];
	}
	return (a<<32)+b;
}

ll hashhash(int cur,int mask) {
	if(memo[cur][mask]<0) {
		vector<ll> v;
		FORR(e,E[cur]) if(mask&(1<<e)) v.push_back(hashhash(e,mask&tmask[cur][e]));
		memo[cur][mask]=tree_normalize(v);
	}
	return memo[cur][mask];
}

int dfsmask(int cur,int pre) {
	if(tmask2[cur][pre]==0) {
		tmask2[cur][pre]=1<<cur;
		FORR(r,E[cur]) if(r!=pre) tmask2[cur][pre] |= dfsmask(r,cur);
	}
	return tmask2[cur][pre];
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	MINUS(memo);
	cin>>N;
	FOR(i,N-1) {
		cin>>x>>y;
		E[x-1].push_back(y-1);
		E[y-1].push_back(x-1);
		A[i]=x-1;
		B[i]=y-1;
		AS[i]=1<<A[i];
		BS[i]=1<<B[i];
	}
	
	FOR(i,N) tmask[19][i]=dfsmask(i,19);
	FOR(i,N-1) {
		tmask[A[i]][B[i]]=dfsmask(B[i],A[i]);
		tmask[B[i]][A[i]]=dfsmask(A[i],B[i]);
	}
	
	for(int mask=1;mask<1<<N;mask++) {
		
		x = __builtin_popcount(mask);
		
		int ok=0;
		FOR(i,N-1) if((mask&AS[i])&&(mask&BS[i])) ok++;
		if(ok!=x-1) continue;
		vector<ll> T;
		FOR(i,N) if(mask&(1<<i)) T.push_back(hashhash(i,mask));
		S[x].push_back(tree_normalize(T));
	}
	
	int ret=0;
	FOR(x,20) {
		sort(ALL(S[x]));
		S[x].erase(unique(ALL(S[x])),S[x].end());
		ret+=S[x].size();
	}
	cout<<ret<<endl;
}

まとめ

本番ハッシュ値判定があまりうまくいかなかった。

広告を非表示にする