UOJ 763:树哈希 ← 链式前向星

【题目来源】
https://uoj.ac/problem/763

【题目描述】
这是一道模板题。
给定一棵以点 1 为根的树,你需要输出这棵树中最多能选出多少个互不
同构的子树。
两棵有根树 T1、T2 同构当且仅当他们的大小相等,且存在一个顶点排列 σ 使得在 T1 中 i 是 j 的祖先当且仅当在 T2 中 σ(i) 是 σ(j) 的祖先。

【输入格式】
第一行一个正整数 n,表示树的点数。  接下来 n−1 行给出树边。每行两个正整数 a,b,表示树上有一条连接点 a 和点 b 的边。

【输出格式】
一行一个正整数,表示最多能选出的互不同构的子树个数。

【输入样例】
10
1 2
1 3
2 4
2 5
3 6
3 7
3 8
8 9
8 10

【输出样例】
4

【限制与约定】
对于所有数据,1≤n≤10^6。

【算法分析】
树哈希,是求解
树的同构问题的典型算法。涉及到的知识点如下所示。
● shift 函数:shift 函数是一个将哈希值向左或向右移动特定位数的操作。在某些哈希算法中,这是一个常见的步骤。具体的 shift 函数实现会根据所使用的哈希算法而变化。

inline ull shift(ull x) {
    x^=x<<11;
    x^=x>>4;
    x^=x<<5;
    x^=x>>14;
    return x;
}

● 快读:https://blog.csdn.net/hnjzsyjyj/article/details/120131534
● 链式前向星:https://blog.csdn.net/hnjzsyjyj/article/details/139369904
● 去重函数 unique:
https://cplusplus.com/reference/algorithm/unique/

#include <bits/stdc++.h>
using namespace std;

const int maxn=100;
int a[maxn];

int main() {
    int n;
    cin>>n;
    for(int i=0; i<n; i++) cin>>a[i];
    sort(a,a+n); //must sort before using unique
    //The number of different elements after removing duplication
    int len=unique(a,a+n)-a;
    cout<<len<<endl;

    return 0;
}

/*
in:
7
1 7 2 7 2 6 5

out:
5
*/

从代码易见,去重函数 unique 使用之前,需利用 sort 对数据进行排序

【算法代码】

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
const int maxn=1e6+7;
const int maxm=maxn<<1;
int h[maxn],e[maxm],ne[maxm],idx;
ull dp[maxn];

inline int read() { //fast read
    int x=0,f=1;
    char c=getchar();
    while(c<'0' || c>'9') { //!isdigit(c)
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0' && c<='9') { //isdigit(c)
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}

inline void add(int a,int b) {
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

inline ull shift(ull x) {
    x^=x<<11;
    x^=x>>4;
    x^=x<<5;
    x^=x>>14;
    return x;
}

void dfs(int u, int fa) {
    dp[u]=1;
    for(int i=h[u]; i!=-1; i=ne[i]) {
        int j=e[i];
        if(j!=fa) {
            dfs(j,u);
            dp[u]+=shift(dp[j]);
        }
    }
}

int main() {
    int n=read();
    memset(h,-1,sizeof(h));
    for(int i=1; i<n; i++) {
        int a=read();
        int b=read();
        add(a,b),add(b,a);
    }
    dfs(1,0);
    sort(dp+1,dp+n+1);
    cout<<unique(dp+1,dp+n+1)-dp-1;

    return 0;
}

/*
in:
10
1 2
1 3
2 4
2 5
3 6
3 7
3 8
8 9
8 10

out:
4
*/




【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/139369904
https://uoj.ac/problem/763/statistics
https://oi-wiki.org/graph/tree-hash/
https://blog.csdn.net/SC_Linno/article/details/121026532
https://www.cnblogs.com/dzbblog/p/18063082

https://www.cnblogs.com/x383494/p/17612666.html
https://www.luogu.com.cn/problem/P5043


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值