BZOJ 3162 / Luogu P4895: 独钓寒江雪 树hash+DP
题意
给出一棵无根树,求本质不同的独立集数模100000000710000000071000000007的值。
n≤500000n\le 500000n≤500000
题解
如果是有根树就好做多了。然而无根树可以找重心作为根,转化为有根树。
那么考虑有根树的本质不同的独立集数怎么求。
直接dpdpdp就行了。用f[i][0]f[i][0]f[i][0]表示iii不选的独立集数,f[i][1]f[i][1]f[i][1]表示iii要选的独立集数。
转移的时候要考虑本质是否不同。做法是树hashhashhash。把子节点的hashhashhash值排序后,值相同的子树拿出来一起考虑,用简单组合数计算。求组合数可以暴力求,总时间复杂度是O(n)O(n)O(n)的。
还有问题就是一颗无根树可能重心在边上,那么我们把这条边新建一个点作为根,同样dpdpdp就行了。求最终答案的时候要特别考虑一下。
时间复杂度O(nlogn)O(n\log n)O(nlogn),因为要排序。
CODE
底数选的好hashhashhash可能随便过,否则就要多搞点信息进去,比如儿子个数什么的。。
#pragma GCC optimize (3)
#include <bits/stdc++.h>
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch; for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 500005;
const int mod = 1e9 + 7;
const ULL p = 998244353;
int n, fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], cnt;
inline void link(int u, int v) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; }
int rt[2];
int dfs(int u, int ff) {
int re = 1, tmp; bool can = 1;
for(int i = fir[u], v; i; i = nxt[i])
if((v=to[i]) != ff) {
re += (tmp = dfs(v, u));
if(tmp<<1 > n) can = 0;
}
if(re<<1 < n) can = 0;
if(can) rt[bool(rt[0])] = u;
return re;
}
ULL hsh[MAXN];
int f[MAXN][2], c[MAXN], inv[MAXN];
inline bool cmp(int i, int j) { return hsh[i] < hsh[j]; }
inline int C(int N, int M) {
int re = 1;
while(M && re) re = 1ll * re * (N--) % mod * inv[M--] % mod;
return re;
}
void dp(int u, int ff) {
f[u][0] = f[u][1] = 1;
hsh[u] = 233333;
for(int i = fir[u], v; i; i = nxt[i])
if((v=to[i]) != ff) dp(v, u);
int cur = 0;
for(int i = fir[u], v; i; i = nxt[i])
if((v=to[i]) != ff) c[++cur] = v;
sort(c + 1, c + cur + 1, cmp);
for(int i = 1, j, v; i <= cur; i = j) {
v = c[i];
for(j = i; j <= cur && hsh[c[j]] == hsh[v]; ++j)
hsh[u] = hsh[u] * p ^ hsh[c[j]];
f[u][0] = 1ll * f[u][0] * C(((f[v][0]+f[v][1])%mod + j-i-1)%mod, j-i) % mod;
f[u][1] = 1ll * f[u][1] * C((f[v][0] + j-i-1)%mod, j-i) % mod;
}
hsh[u] *= p * p;
hsh[u] = (hsh[u] + (ULL)cur + 1ull) * p;
}
inline void pre(int N) {
inv[0] = inv[1] = 1;
for(int i = 2; i <= N; ++i) inv[i] = 1ll * (mod - mod/i) * inv[mod%i] % mod;
}
int main () {
read(n); pre(n);
for(int i = 1, u, v; i < n; ++i) read(u), read(v), link(u, v), link(v, u);
dfs(1, 0);
int r = rt[0];
if(rt[1]) {
r = ++n;
for(int i = fir[rt[0]]; i; i = nxt[i]) if(to[i] == rt[1]) to[i] = r;
for(int i = fir[rt[1]]; i; i = nxt[i]) if(to[i] == rt[0]) to[i] = r;
link(r, rt[0]), link(r, rt[1]);
}
dp(r, 0);
if(rt[1]) {
int ans;
if(hsh[rt[0]] != hsh[rt[1]]) ans = (1ll*f[rt[0]][0]*f[rt[1]][0]%mod + 1ll*f[rt[0]][0]*f[rt[1]][1]%mod + 1ll*f[rt[0]][1]*f[rt[1]][0]%mod) % mod;
else ans = (C(f[rt[0]][0] + 1, 2) + 1ll*f[rt[0]][0]*f[rt[1]][1]%mod) % mod;
printf("%d\n", ans);
}
else printf("%d\n", (f[r][0] + f[r][1]) % mod);
}
BZOJ 3162 / Luogu P4895: 独钓寒江雪 树hash+DP的更多相关文章
- 【BZOJ3162】独钓寒江雪 树同构+DP
[BZOJ3162]独钓寒江雪 题解:先进行树hash,方法是找重心,如果重心有两个,则新建一个虚点将两个重心连起来,新点即为新树的重心.将重心当做根进行hash,hash函数不能太简单,我的方法是: ...
- BZOJ 1564 :[NOI2009]二叉查找树(树型DP)
二叉查找树 [题目描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值,每个结 ...
- BZOJ 1509 逃学的小孩 - 树型dp
传送门 题目大意: 在一棵树中, 每条边都有一个长度值, 现要求在树中选择 3 个点 X.Y. Z , 满足 X 到 Y 的距离不大于 X 到 Z 的距离, 且 X 到 Y 的距离与 Y 到 Z 的距 ...
- BZOJ 1864 三色二叉树 - 树型dp
传送门 题目大意: 给一颗二叉树染色红绿蓝,父亲和儿子颜色必须不同,两个儿子颜色必须不同,问最多和最少能染多少个绿色的. 题目分析: 裸的树型dp:\(dp[u][col][type]\)表示u节点染 ...
- 【题解】Luogu p2014 选课 树型dp
题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一 ...
- BZOJ.2286.[SDOI2011]消耗战(虚树 树形DP)
题目链接 BZOJ 洛谷P2495 树形DP,对于每棵子树要么逐个删除其中要删除的边,要么直接断连向父节点的边. 如果当前点需要删除,那么直接断不需要再管子树. 复杂度O(m*n). 对于两个要删除的 ...
- BZOJ 4042 Luogu P4757 [CERC2014]Parades (树形DP、状压DP)
题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4042 (Luogu) https://www.luogu.org/prob ...
- BZOJ 4417 Luogu P3990 [SHOI2013]超级跳马 (DP、矩阵乘法)
题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=4417 (luogu)https://www.luogu.org/prob ...
- BZOJ 2124等差子序列 线段树&&hash
[题目描述 Description] 给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4<p5<…<pLen<=N(Len& ...
随机推荐
- 016 Android 图片选择器(在选中和未选中的过程中,切换展示图片)
1.目标效果 在选中和未选中的过程中,切换展示图片 2.实现方法 (1)在app--->res--->drawable 右击drawable文件夹右键,new ---->drawab ...
- DateTimeFormatter 的操作与使用 -- 通俗易懂
在上一章我们讲解了LocalDate.LocalTime.LocalDateTime.Instant的操作与使用,下面讲解它们之间是如何进行格式化 DateTimeFormatter这个类它只提供了时 ...
- SpringBoot中使用@Scheduled创建定时任务
SpringBoot中使用@Scheduled创建定时任务 定时任务一般会在很多项目中都会用到,我们往往会间隔性的的去完成某些特定任务来减少服务器和数据库的压力.比较常见的就是金融服务系统推送回调,一 ...
- 2019最新黑链代码expression:隐藏链接代码
之前有收集了一些主流的隐藏链接代码,最近又发现一种相当牛X的代码,如下: 源代码: <div style="position:absolute;left:expression(1-90 ...
- python 之 并发编程(进程池与线程池、同步异步阻塞非阻塞、线程queue)
9.11 进程池与线程池 池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务 池子内什么时候装进程:并发的任务属于计算密集型 池子内什么时候装线程:并发的任务属于I ...
- CH09 开机自动烧录QSPI
版本信息: 版本 REV2018 时间 05/22/2018 XILINX ZYNQ LINUX篇 基于米联MZ7X系列 电子版自学资料 常 ...
- A Story of One Country (Hard) CodeForces - 1181E2 (分治)
大意: 给定$n$个平面上互不相交的矩形. 若一个矩形区域只包含一个矩形或者它可以水平或垂直切成两块好的区域, 那么这个矩形区域是好的. 求判断整个平面区域是否是好的. 分治判断, 可以用链表实现删除 ...
- Mock常用占位符一览
1.随机字符串(类型 , 位数)@string(lower,10) lower : 小写字母upper : 大写字母number : 数字 2.随机int(下限 , 上限)@integer(60, 1 ...
- Security Access Control Strategy && Method And Technology Research - 安全访问控制策略及其方法技术研究
1. 访问控制基本概念 访问控制是网络安全防范和客户端安全防御的重要基础策略,它的主要任务是保证资源不被非法使用.保证网络/客户端安全最重要的核心策略之一. 访问控制包括 入网访问控制 网络权限控制 ...
- Java8新特性 - Java内置的四大核心函数式接口
Java内置的四大核心函数式接口 Consumer:消费型接口 对类型为T的对象应用操作,包含方法:void accept(T t) public class TestLambda02 { publi ...