bzoj

description

你要给一个树上的每个点黑白染色,要求白点不相邻。求本质不同的染色方案数。

两种染色方案本质相同当且仅当对树重新标号后对应节点的颜色相同。

\(n\le 5\times10^5\)

sol

首先考虑没有本质相同那个限制怎么做。

直接设\(f_{i,0/1}\)表示\(i\)点染成黑色/白色时子树内的方案数。

转移很简单:\(f_{i,0}=\prod_j (f_{j,0}+f_{j,1}),f_{i,1}=\prod_j f_{j,0}\)。

先在问题在于本质不同。那么如果重新标号之后同构的话方案数就会多算。

考虑重新标号后重心不会变,于是以重心为根处理子树。如果有两个重心就新建一个点连接这两个点,在输出方案的时候讨论一下即可。

在\(dp\)的时候,对于一个点\(i\)的若干个同构的子树,应该要一起计算贡献,设这种子树染色的方案数是\(x\)(就是\(dp\)值),这样的子树一共有\(k\)棵,那么这就是一个可重组合,方案数为\(\binom{x+k-1}{k}\)。

虽然\(x\)可能会很大,但是显然\(k\)是\(O(n)\)的,所以组合数暴力计算即可。

树\(Hash\)要写对啊qwq。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
#define ull unsigned long long
const int N = 5e5+5;
const int mod = 1e9+7;
const ull base1 = 20020415;
const ull base2 = 20011118;
int n,inv[N],to[N<<1],nxt[N<<1],head[N],cnt,sz[N],w[N],root,rt1,rt2,fg,f[2][N],tmp[N];
ull hsh[N];
void link(int u,int v){
to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void getroot(int u,int fa){
sz[u]=1;w[u]=0;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa){
getroot(to[e],u);sz[u]+=sz[to[e]];
w[u]=max(w[u],sz[to[e]]);
}
w[u]=max(w[u],n-sz[u]);
if (w[u]<w[root]) root=u;
}
int C(int n,int m){
int res=1;
for (int i=n-m+1;i<=n;++i) res=1ll*res*i%mod;
for (int i=1;i<=m;++i) res=1ll*res*inv[i]%mod;
return res;
}
bool cmp(int i,int j){return hsh[i]<hsh[j];}
void dfs(int u,int fa){
sz[u]=f[0][u]=f[1][u]=1;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa) dfs(to[e],u),sz[u]+=sz[to[e]];
int len=0;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=fa) tmp[++len]=to[e];
sort(tmp+1,tmp+len+1,cmp);
for (int i=1,j=1;i<=len;i=j){
while (j<=len&&hsh[tmp[j]]==hsh[tmp[i]]) ++j;
f[0][u]=1ll*f[0][u]*C(f[0][tmp[i]]+f[1][tmp[i]]+j-i-1,j-i)%mod;
f[1][u]=1ll*f[1][u]*C(f[0][tmp[i]]+j-i-1,j-i)%mod;
}
hsh[u]=base2*len+sz[u];
for (int i=1;i<=len;++i)
hsh[u]=(hsh[u]*base1)^hsh[tmp[i]];
}
int main(){
n=gi();inv[0]=inv[1]=1;
for (int i=2;i<=n;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
for (int i=1;i<n;++i){
int u=gi(),v=gi();
link(u,v),link(v,u);
}
w[0]=n;getroot(1,0);getroot(root,0);
for (int e=head[root],lst=0;e;lst=e,e=nxt[e])
if (sz[to[e]]*2==n){
++n;
if (e==head[root]) head[root]=nxt[e];
else nxt[lst]=nxt[e];
for (int i=head[to[e]],Lst=0;i;Lst=i,i=nxt[i])
if (to[i]==root){
if (i==head[to[e]]) head[to[e]]=nxt[i];
else nxt[Lst]=nxt[i];
break;
}
link(n,root);link(root,n);link(n,to[e]);link(to[e],n);
rt1=root;rt2=to[e];root=n;fg=1;break;
}
dfs(root,0);
if (!fg) printf("%d\n",(f[0][root]+f[1][root])%mod);
else if (hsh[rt1]==hsh[rt2]) printf("%d\n",(f[0][root]-C(f[1][rt1]+1,2)+mod)%mod);
else printf("%d\n",(1ll*f[0][rt1]*f[0][rt2]+1ll*f[0][rt1]*f[1][rt2]+1ll*f[1][rt1]*f[0][rt2])%mod);
return 0;
}

[BZOJ3162]独钓寒江雪的更多相关文章

  1. BZOJ3162 独钓寒江雪(哈希+树形dp)

    数独立集显然是可以树形dp的,问题在于本质不同. 假设已经给树确立了一个根并且找到了所有等效(注意是等效而不是同构)子树,那么对转移稍加修改使用隔板法就行了. 关键在于找等效子树.首先将树的重心(若有 ...

  2. [bzoj3162]独钓寒江雪_树hash_树形dp

    独钓寒江雪 题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3162 题解: 首先,如果没有那个本质相同的限制这就是个傻逼题. 直接树形dp ...

  3. bzoj3162独钓寒江雪

    题意 \(n\)阶树,求本质不同的独立集个数 做法 重新编号后重心是不变的,如果有两个重心,可以加个虚点 用树哈希判子树有多少个相同的子树,设某种有\(k\)个,如果原本方案数为\(x\)个 则方案数 ...

  4. liaoliao的四连做第二弹

    liaoliao四连做第一弹 1.bzoj3211: 花神游历各国 由于$10^9$以内的数最多只会被开方$10$次,所以我们可以用线段树维护然后剪枝.. #include <cstdio> ...

  5. Noip前的大抱佛脚----赛前任务

    赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...

  6. 【BZOJ3162】独钓寒江雪(树哈希,动态规划)

    [BZOJ3162]独钓寒江雪(树哈希,动态规划) 题面 BZOJ 题解 忽然翻到这道题目,突然发现就是前几天一道考试题目... 题解: 树哈希,既然只考虑这一棵树,那么,如果两个点为根是同构的, 他 ...

  7. 【BZOJ3162】独钓寒江雪 树同构+DP

    [BZOJ3162]独钓寒江雪 题解:先进行树hash,方法是找重心,如果重心有两个,则新建一个虚点将两个重心连起来,新点即为新树的重心.将重心当做根进行hash,hash函数不能太简单,我的方法是: ...

  8. 【bzoj3162】独钓寒江雪

    *题目描述: *题解: 树哈希+组合数学.对于树的形态相同的子树就一起考虑. *代码: #include <cstdio> #include <cstring> #includ ...

  9. [BZOJ:3162]:独钓寒江雪

    题解: 求本质不同的独立集的个数 首先独立集的个数是很好做的 \(f[u][0/1]\)表示节点\(u\)不选/选的方案数 然后dp就是 \(f[u][0] = f[u][0] * (f[v][0] ...

随机推荐

  1. Integration Services 变量

    如果没有变量,你会发现在ssis里面啥都干不成,和人没有灵魂一样 对系统变量唯一可配置的选项是指定变量在更改值时是否引发事件. 待续

  2. zw版【转发·台湾nvp系列Delphi例程】HALCON union1

    zw版[转发·台湾nvp系列Delphi例程]HALCON union1 unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, ...

  3. 如何有规律的备份 WordPress 博客(转)

    转自:http://blog.wpjam.com/m/backup-your-blog-regularly/   我们都知道备份 WordPress 博客应该是规律性的操作,但是事实上往往我们都会因为 ...

  4. python 字节字符串上的字符串操作

    问题:想在字节字符串上执行普通的文本操作(比如移除,搜索和替换). 解决方案 1)字节字符串同样也支持大部分和文本字符串一样的内置操作.比如: >>> data = b'Hello ...

  5. 常用php操作redis命令整理(五)ZSET类型

    ZADD 向有序集合插入一个元素,元素关联一个数值,插入成功返回1,同时集合元素不可以重复, 如果元素已经存在返回 0 <?php var_dump($redis->zadd(,'A')) ...

  6. 实验五分析system_call中断处理过程

    一.实验要求: 1.使用gdb跟踪分析一个系统调用内核函数 2.根据本周所学知识分析系统调用的过程,从system_call开始到iret结束之间的整个过程,并画出简要准确的流程图 二.实验步骤: 1 ...

  7. 一个好玩的CTF题

    一个CTF的题目,拿来学习学习 玩了好久,再加上学校一堆破事,最近又开始瞎弄了,找了几个CTF的题目,和别人写的一些内容,也当是学习,也当是看完之后的小结.顺便也说一下如果自己拿到这题目会从哪做起. ...

  8. 20145221 《Java程序设计》第五周学习总结

    20145221 <Java程序设计>第五周学习总结 教材学习内容总结 第八章部分 - 异常处理 语法与继承架构 使用try...catch 首先要明确一点:Java中所有错误都会打包为对 ...

  9. UVa 10791 最小公倍数的最小和(唯一分解定理)

    https://vjudge.net/problem/UVA-10791 题意: 输入整数n,求至少两个正整数,使得它们的最小公倍数为n,且这些整数的和最小. 思路: 首先对n进行质因数分解,举个例子 ...

  10. 广播地址设为自己的ip地址,子网掩码设为255.255.255.255 是什么意思

    最近看<构建高性能web站点>(老版)第12章讲LVS-DR的配置,有一段针对Real Server的配置不明所以: ifconfig lo:0 125.12.12.77 broadcas ...