随笔 - 58,  文章 - 0,  评论 - 4,  阅读 - 3251

一、题目描述:

  给你一颗 n 个点的有根树 S,你需要构造一颗 n 个节点的有根树 T

  使得 Tn 颗子树中不与 S 的任意一颗子树同构的数量最大。

  注意,这里是有根树,旋转树之后的同构不算同构。输出 T 的所有边。

  数据范围:1n1×106


 二、解题思路:

  树同构,本能地想到树哈希。而树哈希推荐 OI Wiki 上的写法,不容易被卡。

  容易发现,若一棵子树不与 S 中的任意一颗子树同构,那么他的父亲子树也满足条件。

  所以我们只需要找到最小的一颗子树使得它不与 S 同构,然后往上不断加链就行。

  发现这颗最小的子树一定会很小,因为 S 一共只有 n 颗子树。

  所以我们直接从小到大枚举子树就行。设这颗子树的大小为 V,则时间复杂度 O(n×V)

  那么这颗子树最大有多大呢?考虑 n 个点的子树有多少种不同的形态。

  注意到一颗多叉树唯一对应一颗二叉树(分左右儿子),而一颗二叉树也唯一对应一颗多叉树。

  又注意到一颗二叉树唯一对应一颗完全二叉树,一颗完全二叉树唯一对应一颗二叉树。

  所以就是求一颗 n 个节点的完全二叉树有多少种形态。而这是卡特兰数经典题。

  Cat14=26744401×106,所以树的大小最大为 14。时间复杂度 O(14n)

  考虑怎么不重不漏的枚举树的形态。暴搜枚举每一个点的父亲节点。

  但是这样很明显会枚举到很多同构的树,时间复杂度变为 14!×14。难以通过本题。

  其实只需要做一个小剪枝就可以了,就是枚举的父亲节点顺序单调不降。

  想一下就想明白了,就像那个什么最小表示法一样。于是时间复杂度 O(14n)


 三、完整代码:

复制代码
 1 #include<bits/stdc++.h>
 2 #define V e[i].v
 3 #define N 1000010
 4 #define rep(i,l,r) for(int i=l;i<=r;i++)
 5 using namespace std;
 6 map <int,bool> mp;
 7 mt19937 m(time(0));
 8 int n,mask,ha[N],fa[N];
 9 struct EDGE{
10     int v,nxt;
11 }e[N<<1];
12 int head[N],cnt;
13 void add(int u,int v){
14     e[++cnt].v=v;
15     e[cnt].nxt=head[u];
16     head[u]=cnt;
17 }
18 int shift(int val){
19     val^=mask;
20     val^=val<<13;
21     val^=val>>7;
22     val^=val<<17;
23     val^=mask;
24     return val;
25 }
26 void dfs(int u,int ff){
27     ha[u]=1; for(int i=head[u];i!=-1;i=e[i].nxt)
28         if(V!=ff) dfs(V,u),ha[u]+=ha[V];
29     ha[u]=shift(ha[u]); 
30 }
31 void dfs2(int step,int lim,int num){
32     if(step>num){
33         rep(i,1,num) head[i]=-1; cnt=0;
34         rep(i,2,num) add(fa[i],i); dfs(1,0);
35         if(!mp[ha[1]]){
36             int k=n-num;
37             rep(i,1,k) cout<<i<<" "<<i+1<<'\n';
38             rep(i,2,num) cout<<i+k<<" "<<fa[i]+k<<'\n';
39             exit(0);
40         } return ;
41     }
42     rep(i,lim,step-1) fa[step]=i,dfs2(step+1,i,num);
43 }
44 int main(){
45     ios::sync_with_stdio(false);
46     cin.tie(0);cout.tie(0);
47     cin>>n; mask=m();
48     rep(i,1,n) head[i]=-1;
49     rep(i,1,n-1){
50         int u,v; cin>>u>>v;
51         add(u,v); add(v,u);
52     }
53     dfs(1,0); rep(i,1,n) mp[ha[i]]=1;
54     rep(i,1,n) dfs2(2,1,i);
55     rep(i,2,n) cout<<i-1<<" "<<i<<'\n';
56     return 0;
57 }
复制代码

四、写题心得:

  好,第二次写树哈希,收获经验如下:

  1shift =>Exp++!

  2使=>Exp++!

posted on   trh0630  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示