• 「JSOI2016」独特的树叶(树Hash)


    opening words

    题目链接

    这是我在学玩 树hash 后来做的一道题,似乎 树hash 的题很少,我只找到了这一道,但是却是道省选题,说明省选还是可能出的。

    problem outline

    给定两棵树,其中一棵为 (n) 个节点,另一棵树是在上一棵树的基础上新增了一个叶子节点构成的(一共有 (n+1) 个节点),请你找出新增的是哪个叶子节点,输出编号最小的那一个。 ((n <= 10^5)

    problem solution

    这道题显然是个树同构的题目吧,所以需要用到 树hash。

    因为是无根树,所以我们先利用换根DP求出第一棵树的每个节点做根时的Hash值,用 map 存起来。

    然后对于第二棵树做同样的操作。

    我们发现叶子节点的度数一定是 1,所以我们可以找到哪些是叶子节点,我们发现去掉叶子节点 (u) 的整棵树的hash值就是 (ha[fa[u]] - base*pri[1])

    然后我们就可以在 map 里面找是否可以匹配。

    problem std

    // by longdie 
    #include <bits/stdc++.h> 
    #define ull unsigned long long 
    using namespace std; 
    const int N = 1e5 + 5;  
    const ull base = 1; 
    map<ull, int> p; 
    int n, vis[N*20], tot, pri[N]; 
    struct Tree {
    	ull f[N], ha[N]; 
    	int n, head[N], cnt, pa[N], d[N], siz[N]; 
    	struct edge { int to, next; } e[N<<1]; 
    	void add(int x, int y) {
    		e[++cnt] = (edge){y, head[x]}, head[x] = cnt, d[x]++; 
    	}
    	void dfs0(int u, int fa) {
    		siz[u] = 1, ha[u] = base, pa[u] = fa; 
    		for(register int i = head[u], v; i; i = e[i].next) {
    			v = e[i].to; 
    			if(v == fa) continue; 
    			dfs0(v, u); 
    			siz[u] += siz[v]; 
    			ha[u] += ha[v] * pri[siz[v]]; 
    		}
    	}
    	void dfs1(int u, int fa) {
    		if(u != 1) {
    			f[u] = ha[u] + (f[fa] - ha[u]*pri[siz[u]])*pri[n-siz[u]]; 
    		}
    		for(register int i = head[u], v; i; i = e[i].next) {
    			v = e[i].to; 
    			if(v == fa) continue; 
    			dfs1(v, u); 
    		}
    	}
    	void solve(int m) {
    		n = m; 
    		for(register int i = 1, x, y; i < n; ++i) {
    			scanf("%d%d", &x, &y), add(x, y), add(y, x); 
    		}
    		dfs0(1, 0); 
    		f[1] = ha[1]; 
    		dfs1(1, 0); 
    	} 
    } a, b; 
    signed main() {
    	scanf("%d", &n);
    	for(register int i = 2; i <= 2e6 && tot <= n+1; ++i) {
    		if(!vis[i]) pri[++tot] = i; 
    		for(register int j = 1; j <= tot && pri[j]*i <= 2e6; ++j) {
    			vis[i * pri[j]] = 1; 
    			if(i % pri[j] == 0) break; 
    		}
    	}  
    	a.solve(n);
    	for(register int i = 1; i <= n; ++i) {
    		p[a.f[i]] = 1; 
    	}
    	b.solve(n + 1); 
    	for(register int i = 1; i <= n + 1; ++i) {
    		if(b.d[i] != 1) continue; 
    		int u = b.e[b.head[i]].to; 
    		ull tmp = b.f[u] - 2; 
    		if(p.find(tmp) != p.end()) return printf("%d
    ", i), 0; 
    	}	
    	return 0; 
    }
    
  • 相关阅读:
    linux 网卡配置详情
    linux ftp 添加用户及权限管理
    mysql 权限管理
    linux ftp 安装及相关命令
    linux find 命令
    linux yum 安装及卸载
    linux svn 安装
    cssText方式写入css
    addLoadEvent
    mobile体验效果:增加点击后反馈
  • 原文地址:https://www.cnblogs.com/longdie/p/14514203.html
Copyright © 2020-2023  润新知