同构树计数 [构造]

本文探讨了如何计算同构树的数量,重点在于理解一棵树的同构数量可以表示为各子树大小阶乘的乘积,并通过实例展示了如何将给定数值分解为互不对称的菊花图子树。文章分为正解部分和实现部分,详细解释了如何避免子树对称并进行计算。

同 构 树 计 数 同构树计数


1 ≤ T ≤ 1 0 4 , 1 ≤ K ≤ 1 0 18 1 \le T \le 10^4, 1 \le K \le 10^{18} 1T104,1K1018


正 解 部 分 \color{red}{正解部分}

首先要知道一棵树的同构数量 x x x 可以表示为 x = ∏ a i !     ( a i ∈ N ) x = \prod a_i! \ \ \ (a_i \in N) x=ai!   (aiN),
如下图为一个例子,

!/

于是考虑将 K K K 分解为若干个菊花图, 使得两两菊花图之间互不对称, 使得每个菊花子树大小的阶乘乘起来为 K K K, 如下图所示,


为了防止子树之间对称, 在链后方加两个点 .


实 现 部 分 \color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register
typedef long long ll;

const int maxn = 100005;

int cnt;
int flag;
int size[maxn];

ll K;
ll fac[maxn];

void DFS(ll now, int k){
        if(now == 1 || flag){ flag = 1; return ; }
        for(reg int i = k; i >= 2; i --){
                if(now % fac[i]) continue ;
                size[++ cnt] = i; DFS(now/fac[i], i);
                if(flag) return ; size[cnt --] = 0;
        }
}

void Work(){
        scanf("%lld", &K);
        if(K == 1){ printf("1\n"); return ; }
        flag = cnt = 0; DFS(K, 19);
        if(!flag){ printf("-1\n"); return ; }
        int sum = 2;
        for(reg int i = 1; i <= cnt; i ++) sum += size[i] + 1;
        printf("%d\n", sum);
        for(reg int i = 1; i < cnt; i ++) printf("%d %d\n", i, i + 1);
        int node_cnt = cnt;
        for(reg int i = 1; i <= cnt; i ++)
                for(reg int j = 1; j <= size[i]; j ++) printf("%d %d\n", i, ++ node_cnt);
        printf("%d %d\n", cnt, ++ node_cnt);
        printf("%d %d\n", node_cnt, node_cnt + 1);
}

int main(){
        fac[0] = 1; for(reg int i = 1; i <= 20; i ++) fac[i] = 1ll*fac[i-1]*i;
        int T;
        scanf("%d", &T);
        while(T --) Work();
        return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值