【HYSBZOJ】【哈希】【換根DP】4754 [Jsoi2016]獨特的樹葉

HYSBZOJ 4754 [Jsoi2016]獨特的樹葉

題目大意

◇題目傳送門◆

分析

這道題顯然考察的是樹哈希。

我這裏採用的方法是:

f(u)=1+vson(u)f(v)×Psizv f(u)=1+\sum_{v\in son(u)}f(v)\times P_{siz_v}

其中PP表示質數序列,sizvsiz_v表示以vv爲根的子樹的大小,f(u)f(u)表示以uu爲根的子樹的哈希值。

現在的要求就是對於AABB中的每個節點,求出以該節點爲根的整棵樹的哈希值。

這樣的話,我們考慮換根。

g(u)g(u)爲去掉uu這棵子樹後的整棵樹的哈希值,不難得出:

g(u)=h(u)f(u)×Psizu g(u)=h(u)-f(u)\times P_{siz_u}

其中h(u)h(u)表示以當前節點爲根,整棵樹的哈希值。h(u)h(u)的表達式我們也不難得出:

h(u)={f(u)u=1f(u)+g(u)×Psiz1sizuu1 h(u)=\begin{cases}f(u)&u=1\\f(u)+g(u)\times P_{siz_1-siz_u}&u\neq1\end{cases}

這裏我用節點11爲根。

然後我們將BB的所有哈希值塞進set或者map裏面去。再枚舉AA中的每一個點,將它接到一個虛擬的點上面去,算出新的哈希值1+h(u)×PN1+h(u)\times P_N,找到最小的那個節點就是了。

總時間複雜度爲O(NlogN)O(N\log N)

參考代碼

#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef unsigned long long ull;
const int Maxn = 1e5;
const int P = 1e7;

ull SEED[Maxn + 5];
void calcSEED() {
	static bool ispri[P + 5];
	int cnt = 0;
	for(int i = 2; i <= P; i++) {
		if(!ispri[i])
			SEED[++cnt] = i;
		for(int j = 1; i * SEED[j] <= P && j <= cnt; j++) {
			ispri[i * SEED[j]] = true;
			if(i % SEED[j] == 0) break;
		}
	}
}

struct Graph {
	struct Edge {
		int to;
		Edge *nxt;
	};
	Edge pool[Maxn * 2 + 5];
	Edge *G[Maxn + 5], *ecnt;
	void init() {
		memset(G, 0, sizeof G);
		ecnt = &pool[0];
	}
	void addedge(int u, int v) {
		Edge *p = ++ecnt;
		p->to = v, p->nxt = G[u];
		G[u] = p;
	}
	ull hashval[Maxn + 5];
	ull f[Maxn + 5], g[Maxn + 5];
	int siz[Maxn + 5];
	void dfs1(int u, int fa) {
		f[u] = siz[u] = 1; 
		for(Edge *p = G[u]; p != NULL; p = p->nxt) {
			int v = p->to;
			if(v == fa) continue;
			dfs1(v, u);
			siz[u] += siz[v], f[u] += SEED[siz[v]] * f[v];
		}
	}
	void dfs2(int u, int fa) {
		if(fa != 0) g[u] = hashval[fa] - f[u] * SEED[siz[u]];
		if(fa == 0) hashval[u] = f[u];
		else hashval[u] = f[u] + g[u] * SEED[siz[1] - siz[u]];
		for(Edge *p = G[u]; p != NULL; p = p->nxt) {
			int v = p->to;
			if(v == fa) continue;
			dfs2(v, u);
		}
	}
	void calc_hash() {
		dfs1(1, 0);
		dfs2(1, 0);
	}
};

int N;

Graph A, B;
map<ull, int> s;
int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	calcSEED();
	A.init(), B.init();
	scanf("%d", &N);
	for(int i = 1; i < N; i++) {
		int u, v;
		scanf("%d %d", &u, &v);
		A.addedge(u, v), A.addedge(v, u);
	}
	for(int i = 1; i <= N; i++) {
		int u, v;
		scanf("%d %d", &u, &v);
		B.addedge(u, v), B.addedge(v, u);
	}
	A.calc_hash(), B.calc_hash();
	for(int i = 1; i <= N + 1; i++)
		if(!s.count(B.hashval[i])) s[B.hashval[i]] = i;
		else s[B.hashval[i]] = min(s[B.hashval[i]], i);
	for(int i = 1; i <= N; i++)
		if(s.count(1 + A.hashval[i] * SEED[N])) {
			printf("%d\n", s[1 + A.hashval[i] * SEED[N]]);
			break;
		}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章