Problem:
F: 木を隠すのなら森の中

物語

「木はいいです」
喫茶店「タートルハウス」を経営するチコちゃんは、木に並々ならぬ愛情を持っている。なんでも、木の不動のあり方に心が洗われるらしい。。。そんなチコちゃんの木への愛情を確かめるため、同じ喫茶店でバイトをしているココロちゃんはチコちゃんにいたずらを仕掛けることにした。

ココロちゃんは、チコちゃんのために森を用意して、喫茶店近くに生えていた木を何本か森の中に隠し、チコちゃんの木への愛情を試すことにした。具体的には、チコちゃんに、隠した木を森の中から見つけてもらうことにした。木をこよなく愛するチコちゃんなら、一度見たことがある木は、心の洗われ方の違いでわかるのだが、木を移動してしまったため、木の不動のあり方が変わってしまい、どの木が喫茶店近くにあった木かわからなくなってしまった。しかも、ドジなココロちゃんも答えの木がどれか忘れてしまった！このままでは答えの木を元の場所に戻せないので、ココロちゃんがチコちゃんに怒られてしまう。そこで、優しいプログラマーの皆さんでココロちゃんに答えの木が何本あるのか教えてあげるプログラムを書いてあげよう。

問題

入力として、2つのグラフG_1とG_2が与えられる。G_1は森、つまり閉路のないグラフ（連結とは限らない）で、G_2は木、つまり閉路のない連結なグラフである。このとき、G_1がG_2と同型な連結成分をいくつ持つのかを求めよ

注1）グラフG_1のある連結成分H_1に含まれる任意の2頂点u, vに対して、u, vが隣接する場合にのみ、G_2中の2頂点f(u), f(v)が隣接するような全単射fが存在するとき、H_1は、G_2と同型なG_1の連結成分であるという。

入力

入力は以下の形式で与えられる。

N_1 M_1
u_1 v_1
...
u_{M_1} v_{M_1}
N_2
w_1 x_1
...
w_{N_2 - 1} x_{N_2 - 1}

1行目では、森G_1の頂点数N_1（1 \leq N_1 \leq 300,000）と辺数M_1（0 \leq M_1 \leq N_1 - 1）が与えられる。続くM_1行では、i行目でG_1のi番目の辺e_iが与えられる(1 \leq i \leq M_1)。e_iは2頂点u_iとv_iをつなぐ辺である。（1 \leq u_i, v_i \leq N_1, u_i \neq v_i）次の行には木G_2の頂点数N_2（1 \leq N_2 \leq 30,000）が与えられる。続くN_2 - 1行では、j行目でG_2のj番目の辺e_jが与えられる(1 \leq j \leq N_2 - 1)。e_jは2頂点w_jとx_jをつなぐ辺である。（1 \leq w_j, x_j \leq N_2, w_j \neq x_j）

出力

G_1の持つG_2と同型な連結成分の数を出力せよ。

入力例1

6 4
1 2
2 3
2 4
5 6
4
2 3
3 1
3 4

出力例1

1

入力例2

14 9
5 8
5 11
10 14
6 4
13 9
3 14
1 2
6 12
7 9
3
3 1
2 1

出力例2

4

Solution in C++ (judge verdict: Time Limit Exceeded)

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <string>
#include <algorithm>
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <sstream>
#include <iostream>
#include <functional>
#include <iostream>
#define MOD 1000000007LL
using namespace std;
typedef long long ll;
typedef pair<int,int> P;

int n[2],m[2];
vector<int> G[2][400001];
int dist[400001];
int dist2[400001];
bool used[400001];
bool calced[400001];
vector<int> root[2];
vector<int> vs;
map<string,int> mp;
vector<int> vp;

string make_str(int v){
	string s="";
	while(v>0){
		s+=(v%10)+'0';
		v/=10;
	}
	reverse(s.begin(),s.end());
	return s;
}

vector<int> s[400001];

int calc(int t,int v){
	calced[v]=true;
	s[v].clear();
	for(int i=0;i<(int)G[t][v].size();i++){
		int nv=G[t][v][i];
		if(!calced[nv]){
			int val=calc(t,nv);
			if(val==-1)return -1;
			s[v].push_back(val);
		}
	}
	if(s[v].size()>0)sort(s[v].begin(),s[v].end());
	string str="";
	for(int i=0;i<s[v].size();i++){
		str+=make_str(s[v][i]);
	}
	s[v].clear();
	if(str.size()==0){
		str+='0';
	}
	if(mp.find(str)!=mp.end()){
		return mp[str];
	}else{
		if(t==0)return -1;
		int siz=mp.size();
		mp[str]=siz+1;
		return siz+1;
	}
	return -1;
}

void bfs(int t,int s){
	queue<int> que;
	memset(dist,-1,sizeof(dist));
	vs.clear();
	que.push(s);
	dist[s]=0;
	while(que.size()){
		int v=que.front();
		que.pop();
		vs.push_back(v);
		for(int i=0;i<G[t][v].size();i++){
			if(dist[G[t][v][i]]==-1){
				dist[G[t][v][i]]=dist[v]+1;
				que.push(G[t][v][i]);
			}
		}
	}
}

void solve(){
	bfs(1,0);
	int maxi=-1;
	int tgt=0;
	for(int i=0;i<n[1];i++){
		if(dist[i]>maxi){
			maxi=dist[i];
			tgt=i;
		}
	}
	bfs(1,tgt);
	maxi=-1;
	for(int i=0;i<n[1];i++){
		if(dist[i]>maxi){
			maxi=dist[i];
			tgt=i;
		}
	}
	for(int i=0;i<n[1];i++){
		dist2[i]=dist[i];
	}
	bfs(1,tgt);
	for(int i=0;i<n[1];i++){
		if(maxi%2==0){
			if(dist[i]==maxi/2 && dist2[i]==maxi/2)root[1].push_back(i);
		}else{
			if(dist[i]==(maxi+1)/2 && dist2[i]==maxi/2)root[1].push_back(i);
			if(dist[i]==maxi/2 && dist2[i]==(maxi+1)/2)root[1].push_back(i);
		}
	}
	for(int i=0;i<root[1].size();i++){
		memset(calced,false,sizeof(calced));
		vp.push_back(calc(1,root[1][i]));
	}
	int res=0;
	for(int i=0;i<n[0];i++){
		if(!used[i]){
			root[0].clear();
			bfs(0,i);
			maxi=-1;
			tgt=0;
			for(int j=0;j<vs.size();j++){
				int v=vs[j];
				if(dist[v]>maxi){
					maxi=dist[v];
					tgt=v;
				}
				used[v]=true;
			}
			if(vs.size()!=n[1])continue;
			bfs(0,tgt);
			maxi=-1;
			for(int j=0;j<vs.size();j++){
				int v=vs[j];
				if(dist[v]>maxi){
					maxi=dist[v];
					tgt=v;
				}
				dist2[v]=dist[v];
			}
			bfs(0,tgt);
			for(int j=0;j<vs.size();j++){
				int v=vs[j];
				if(maxi%2==0){
					if(dist[v]==maxi/2 && dist2[v]==maxi/2)root[0].push_back(v);
				}else{
					if(dist[v]==(maxi+1)/2 && dist2[v]==maxi/2)root[0].push_back(v);
					if(dist[v]==maxi/2 && dist2[v]==(maxi+1)/2)root[0].push_back(v);
				}
			}
			if(root[0].size()>=3)return;
			bool flag=false;
			for(int j=0;j<root[0].size();j++){
				int v1=root[0][j];
				memset(calced,false,sizeof(calced));
				//if(n[0]==300000)return;
				int s1=calc(0,v1);
				for(int k=0;k<vp.size();k++){
					if(s1==vp[k])flag=true;
				}
				//if(n[0]==300000)return;
			}
			if(flag)res++;
		}
	}
	printf("%d\n",res);
}

int main(void){
	scanf("%d%d",&n[0],&m[0]);
	for(int i=0;i<m[0];i++){
		int f,t;
		scanf("%d%d",&f,&t);
		f--;
		t--;
		G[0][f].push_back(t);
		G[0][t].push_back(f);
	}

	scanf("%d",&n[1]);
	m[1]=n[1]-1;
	for(int i=0;i<m[1];i++){
		int f,t;
		scanf("%d%d",&f,&t);
		f--;
		t--;
		G[1][f].push_back(t);
		G[1][t].push_back(f);
	}
	solve();
	return 0;
}