BZOJ 3162: 独钓寒江雪 树的同构 + 组合 + 计数
Description
Input
Output
求一棵树编号序列不同的方案数:
令 $f[u],g[u]$ 分别表示 $u$ 选/不选 的方案数.
则 $f[u]=\prod_{v\in son[u]}g[v]$,$g[u]=\prod_{v\in son[u]}g[v]+f[v]$.
然而如果要求本质不同,那么那些子树结构相同的就会算重.
假设有 $k$ 个儿子树形态相同,每一个儿子可选的方案为 $h$.
则我们要求给每一个儿子都分一种方案的方案数.
即有 $m$ 个相同的盒子,有 $k$ 种球,求给每一个盒子分配一个球(可重复)的方案数.
这个直接用可重集公式即可,即 $C_{k+m-1}^{m}$.
如何求得所有形态相同得子树呢?
这棵树无论如何旋转,重心都是不变的,以重心(或两重心之间连一个点)为根,进行树哈希+树形DP即可.
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
typedef long long ll;
const int N=500003,mod=1000000007,mul=20011118,ha=20011118,con=2019;
vector<int>rt;
ll F[N],G[N];
int n,edges,M,root;
int hd[N],to[N<<1],nex[N<<1],mx[N],siz[N],Hash[N],sta[N];
ll qpow(ll base,ll k)
{ll tmp=1ll; for(;k;base=(base*base)%mod,k>>=1) if(k&1) tmp=(tmp*base)%mod; return tmp;
}
ll inv(int a) { return qpow((ll)a, (ll)mod-2); }
bool cmp(int a,int b)
{return Hash[a]<Hash[b];
}
inline void addedge(int u,int v)
{nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
void getroot(int u,int ff)
{siz[u]=1,mx[u]=0; for(int i=hd[u];i;i=nex[i]) if(to[i]!=ff) getroot(to[i],u),siz[u]+=siz[to[i]],mx[u]=max(mx[u],siz[to[i]]); M=min(M,mx[u]=max(mx[u],n-siz[u]));
}
ll C(int a,int b)
{ll tmp=1; for(int i=a-b+1;i<=a;++i) tmp=(1ll*i*tmp)%mod; for(int i=1;i<=b;++i) tmp=(1ll*inv(i)*tmp)%mod; return tmp;
}
void calc(int u,int ff)
{ int i,j,tmp=0; Hash[u]=2019; for(i=hd[u];i;i=nex[i]) if(to[i]!=ff) calc(to[i],u); sta[0]=0; for(i=hd[u];i;i=nex[i]) if(to[i]!=ff) sta[++sta[0]]=to[i]; sort(sta+1,sta+1+sta[0],cmp); for(i=1;i<=sta[0];++i) Hash[u]=((ll)(Hash[u]*mul)^Hash[sta[i]])%ha; F[u]=G[u]=1ll; for(i=1;i<=sta[0];i=j+1) { j=i; while(j<sta[0]&&Hash[sta[j+1]]==Hash[sta[j]]) ++j; F[u]=(F[u]*C(G[sta[i]]+j-i, j-i+1))%mod; G[u]=(G[u]*C(G[sta[i]]+F[sta[i]]+j-i, j-i+1))%mod; }
}
int main()
{ int i,j; // setIO("input"); scanf("%d",&n); for(i=1;i<n;++i) {int x,y; scanf("%d%d",&x,&y),addedge(x,y),addedge(y,x); }M=n,getroot(1,0); for(i=1;i<=n;++i) if(mx[i]==M) rt.push_back(i); if(rt.size()==2) { int pre; root=++n; addedge(n,rt[0]),addedge(n,rt[1]); if(to[hd[rt[0]]]==rt[1]) hd[rt[0]]=nex[hd[rt[0]]]; else {for(pre=i=hd[rt[0]];i;pre=i,i=nex[i]) if(to[i]==rt[1]) { nex[pre]=nex[i]; break; } }if(to[hd[rt[1]]]==rt[0]) hd[rt[1]]=nex[hd[rt[1]]]; else {for(pre=i=hd[rt[1]];i;pre=i,i=nex[i]) if(to[i]==rt[0]) { nex[pre]=nex[i]; break; } } }else root=rt[0]; calc(root,0); if(rt.size()==1) printf("%lld\n",(F[root]+G[root])%mod); else { int a=rt[0],b=rt[1]; if(Hash[a]==Hash[b]) printf("%lld\n",(G[root]-C(F[a]+1,2)+mod)%mod); else printf("%lld\n", (((F[a]*F[b])%mod) + ((F[a]*G[b])%mod) + ((G[a]*G[b])%mod)%mod)); } return 0;
}
转载于:https://www.cnblogs.com/guangheli/p/11378318.html
BZOJ 3162: 独钓寒江雪 树的同构 + 组合 + 计数相关推荐
- BZOJ 3162 独钓寒江雪(树同构计数)
给定一棵无根树,求其中本质不同的独立集的个数. 无根树同构. 转化成以重心为根的有根树,如果重心有两个,就在这两个重心之间插入一个点与这两个重心连边,这个点作为新的重心. 然后就成了有根树同构过程树形 ...
- bzoj 3162: 独钓寒江雪 树哈希+树形dp
题意 给出一棵无标号无根树,问本质不同的最大独立集数量.答案模1e9+7. n<=500000 分析 对于一般的情况,我们可以先找出树的重心作为根,然后进行树形dp.这样做有什么好处呢?通过根的 ...
- 无根树的同构:Hash最小表示法(bzoj 4337: BJOI2015 树的同构)
这里的同构是指: 对于两棵树A, B,如果能通过重新标号使得两棵树完全相同,则称树A和B同构 Hash最小表示法步骤: ①暴力每个节点为根 ②对于当前根x,对树进行DFS ③DFS时对每个节点维护一个 ...
- [树hash]BZOJ 4337——BJOI2015 树的同构
题目梗概 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同构的. 有M个有根树,请你把它们按同构关系分成若干个等价类. 解题思路 第一次写树has ...
- BZOJ.4337.[BJOI2015]树的同构(树哈希)
BZOJ 洛谷 \(Description\) 给定\(n\)棵无根树.对每棵树,输出与它同构的树的最小编号. \(n及每棵树的点数\leq 50\). \(Solution\) 对于一棵无根树,它的 ...
- BZOJ 4517 浅谈错位排列组合计数
世界真的很大 讲道理本来5分钟的水题卡了我半个小时一直RE 原因竟是因为cout? 改成printf就对了??EXM? 看题先: description: 求有多少种长度为 n 的序列 A,满足以下条 ...
- BZOJ 4337: BJOI2015 树的同构|Hash
此题三战三卒--!! 一卒:没有考虑连边的顺序. 二卒:用排序解决了连边的顺序,然后用重心似乎搞不了-- 三卒:考虑到rp问题,换了换hash的数..三卒! 扔了重心终于A掉了 可以用括号序列,然后h ...
- 【BZOJ - 4337】BJOI2015 树的同构(树哈希)
题干: 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的 ...
- bzoj 4337 树的同构
4337: BJOI2015 树的同构 Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树 ...
最新文章
- 近期Freecodecamp问题总结
- Plant Com:中科院遗传发育所白洋组开发定量检测宿主微生物组的HA-QAP技术(王二涛点评)...
- 关于IOCP完成端口的文章
- idea无法找到主启动类_idea 启动springboot项目报找不到主类
- HotSpot源码(一):Docker与虚拟机的区别,class字节码解析,linux内核源码下载地址,Yacc与Lex快速入门
- swift string转int_swift中结构体和类的区别(值类型和引用类型的区别)
- java date 时分秒_java Date 获得时分秒代码
- python语法学习_python语法学习笔记
- javaWeb保存时间到数据库
- Android Studio Cmake C++ JNI demo
- python程序控制结构与分支的实验报告_Python程序设计实验报告三:分支结构程序设计...
- xp计算机位数,XP查看电脑系统版本是32位还是64位的方法
- Photoshop CC 2019暂存盘不足无法打开
- onkeyup+onafterpaste 只能输入数字和小数点--转载
- 视觉SLAM | OKVIS
- PTX-NPs 纳米粒子修饰紫杉醇/与桦木酸PEG/邻硝基苯丙酸紫杉醇偶联物的制备
- python找与7相关的数字_Python 入门系列 —— 7. 数字类型介绍
- 给 木子健康管理室 添加微信公众号 并制作一条 图文消息
- 守望先锋服务器修改,守望先锋开发者访谈:关于自定义的服务器
- unreal 用于三维展示的改造 建筑 模型展示