zoukankan      html  css  js  c++  java
  • [BZOJ4754] JSOI2016 独特的树叶

    问题描述

    JYY有两棵树A和B:树A有N个点,编号为1到N;树B有N+1个点,编号为1到N+1。JYY知道树B恰好是由树A加上一个叶节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是树B中的哪一个叶节点呢?

    输入格式

    输入一行包含一个正整数N。

    接下来N-1行,描述树A,每行包含两个整数表示树A中的一条边;

    接下来N行,描述树B,每行包含两个整数表示树B中的一条边。

    1≤N≤10^5

    输出格式

    输出一行一个整数,表示树B中相比树A多余的那个叶子的编号。如果有多个符合要求的叶子,输出B中编号最小的

    那一个的编号

    样例输入

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

    样例输出

    1

    链接

    BZOJ

    解析

    首先,我们能够看出来这个问题本质上是要判断树的重构问题。对于此类问题,我们一般采用树哈希的方法来解决。对于第一棵树,如下操作:

    • 叶子结点的哈希值为0。
    • 对于节点x的所有子节点y都有h[x]^=h[y]*p+size[y],其中size表示子树大小,p是选取的任意满足要求的质数。这里采用unsigned long long代替取模。

    由于题目中需要知道以每个点为根节点的树的哈希值,我们可以利用换根DP的思想,得到我们想要的东西。

    对于第二棵树,由于叶子结点可能是新加进来的,我们随便选择一个非叶子结点作为根,执行和第一棵树一样的操作。然后对于每一个叶子节点,如果它的父节点为根节点时的哈希值在消去该叶子节点的影响之后在第一棵树的所有哈希值中出现过,这个叶子节点就是我们要求的节点。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <map>
    #define N 100002
    using namespace std;
    const unsigned long long p=2333333;
    int head[N],ver[N*2],nxt[N*2],d[N],l;
    int n,i,size[N],fa[N],cnt,leaf[N];
    unsigned long long h[N],hash[N];
    map<unsigned long long,bool> m;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y)
    {
    	l++;
    	ver[l]=y;
    	nxt[l]=head[x];
    	head[x]=l;
    }
    void dfs(int x,int pre)
    {
    	h[x]=0;fa[x]=pre;size[x]=1;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre){
    			dfs(y,x);
    			size[x]+=size[y];
    			h[x]^=h[y]*p+size[y];
    		}
    	}
    }
    void dp(int x,int pre,int root)
    {
    	if(x!=root) hash[x]=h[x]^((hash[pre]^(h[x]*p+size[x]))*p+n-size[x]);
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre) dp(y,x,root);
    	}
    }
    int main()
    {
    	n=read();
    	for(i=1;i<n;i++){
    		int u=read(),v=read();
    		insert(u,v);
    		insert(v,u);
    	}
    	dfs(1,0);
    	hash[1]=h[1];
    	dp(1,0,1);
    	for(i=1;i<=n;i++) m[hash[i]]=1;
    	memset(head,0,sizeof(head));
    	l=0;n++;
    	for(i=1;i<n;i++){
    		int u=read(),v=read();
    		insert(u,v);
    		insert(v,u);
    		d[u]++;d[v]++;
    	}
    	int root;
    	for(i=1;i<=n;i++){
    		if(d[i]==1) leaf[++cnt]=i;
    		else root=i;
    	}
    	dfs(root,0);
    	hash[root]=h[root];
    	dp(root,0,root);
    	for(i=1;i<=cnt;i++){
    		if(m.count(hash[fa[leaf[i]]]^1)){
    			printf("%d
    ",leaf[i]);
    			break;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    shell tr命令的使用
    linux find prune排除某目录或文件
    在vue中使用axios发送post请求,参数方式
    webpack官网demo起步中遇到的问题
    css中盒子模型与box-sizing属性
    jquery获得 url的变量
    17-js观察者模式
    基于Jquery ui 可复用的酒店 web页面选择入住日期插件
    webkit浏览器下改变滚动条样式
    用户登录时,禁止chrome提示用户保存密码
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12292764.html
Copyright © 2011-2022 走看看