bzoj4784 [Zjoi2017]仙人掌
Description
Input
Output
Sample Input
3 2
1 2
1 3
5 4
1 2
2 3
2 4
1 5
Sample Output
2
8
对于第一组样例合法加边的方案有 {}, {(2,3)},共 2 种。
正解:仙人掌$DP$
这题好难啊。。我看题解都看了好久才看懂。。
先给两个博客:http://blog.csdn.net/akak__ii/article/details/65935711
ljh2000:http://www.cnblogs.com/ljh2000-jump/p/6613829.html
首先特判不是仙人掌的情况,只要判每个点到达根的路径是否大于$2$条就行了。
然后我们可以先把环拆掉,也就是把环边和对应的那个点与它父亲断开,因为环是不会对答案造成贡献的。然后这个仙人掌就会变成一个森林。于是我们就成功地把仙人掌$DP$变成了树形$DP$。我们单独考虑每棵树的答案,乘法原理一下就好。
然后就是对于每棵树统计答案了。
对于一个点$x$,我们设$f[x]$表示$x$这棵子树连边形成仙人掌的方案数。我们发现,可以分为两种情况:
1,$x$这棵子树一定不与祖先连边,这个是根的情况。
2,$x$这棵子树可能与祖先连边,这个是除了根以外其他点的情况。
对于第1种情况,我们把$x$所有的儿子$f[v]$都乘起来,并且我们计算一下$x$的儿子互相连边的情况,再乘起来就行了。
对于$x$的儿子互相连边的情况,我们可以找到一个规律。我们设$g[i]$表示$i$个儿子互相连边的合法方案数,那么$g[i]=g[i-1]+(i-1)*g[i-2]$。
这是怎么来的呢?我们考虑一下,如果第$i$个点不与其他点连边,那么方案数就是$g[i-1]$,否则,第$i$个点与第$j$个点连边,那么第$j$个点肯定不能与其他点连边,所以方案数是$g[i-2]$,总共有$i-1$种情况,所以$g[i]=g[i-1]+(i-1)*g[i-2]$。那么我们设$x$有$tot$个儿子,于是$f[x]=\prod f[v]*g[tot]$。
那么现在我们只要考虑第二种情况了。其实仔细想想,就是$x$的所有儿子$f[v]$相乘,再乘上$g[tot+1]$就行了。因为这就是$tot+1$个点互相连边的情况。于是$f[x]=\prod f[v]*g[tot+1]$。
于是这道题我们就完美地解决了。
//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define rhl (998244353)
#define inf (1<<30)
#define M (1000010)
#define N (500010)
#define il inline
#define RG register
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) using namespace std; struct edge{ int nt,to; }G[*M];
struct node{ int i,d; }a[N]; int head[N],fa[N],dfn[N],dep[N],lu[N],n,m,cnt;
ll f[N],g[N],ans; il int gi(){
RG int x=,q=; RG char ch=getchar();
while ((ch<'' || ch>'') && ch!='-') ch=getchar();
if (ch=='-') q=-,ch=getchar();
while (ch>='' && ch<='') x=x*+ch-,ch=getchar();
return q*x;
} il void insert(RG int from,RG int to){
G[++cnt]=(edge){head[from],to},head[from]=cnt; return;
} il int cmpd(const node &a,const node &b){ return a.d<b.d; } il void pre(){ //预处理g数组
g[]=g[]=;
for (RG int i=;i<=;++i) g[i]=(g[i-]+(i-)*g[i-])%rhl;
return;
} il void dfs(RG int x,RG int p){
fa[x]=p,dfn[x]=++cnt,dep[x]=dep[p]+;
for (RG int i=head[x],v;i;i=G[i].nt){
v=G[i].to; if (dfn[v]) continue;
dfs(v,x);
}
return;
} il void dp(RG int x,RG int rt){
lu[x]=-,f[x]=; RG int tot=,v;
for (RG int i=head[x];i;i=G[i].nt){
v=G[i].to; if (v==fa[x] || lu[v]!=) continue;
tot++; dp(v,); f[x]=f[x]*f[v]%rhl;
}
if (!rt) f[x]=f[x]*g[tot+]%rhl;
else f[x]=f[x]*g[tot]%rhl;
return;
} il void work(){
n=gi(),m=gi(),cnt=;
for (RG int i=;i<=n;++i) lu[i]=fa[i]=dep[i]=dfn[i]=head[i]=;
for (RG int i=,u,v;i<=m;++i) u=gi(),v=gi(),insert(u,v),insert(v,u);
cnt=; dfs(,);
for (RG int i=,u,v;i<=m;++i){ //统计每个点到根的路径数
u=G[i<<].to,v=G[i<<|].to;
if (dfn[u]<dfn[v]) swap(u,v);
while (u!=v){
if (lu[u]==){ printf("0\n"); return; }
lu[u]++,u=fa[u];
}
}
for (RG int i=;i<=n;++i) a[i].i=i,a[i].d=dep[i];
sort(a+,a+n+,cmpd); ans=;
for (RG int i=,x;i<=n;++i){
x=a[i].i; if (lu[x]==-) continue;
dp(x,); ans=ans*f[x]%rhl;
}
printf("%lld\n",ans); return;
} int main(){
File("cactus");
pre(); RG int T=gi();
while (T--) work();
return ;
}
bzoj4784 [Zjoi2017]仙人掌的更多相关文章
- [BZOJ4784][ZJOI2017]仙人掌(树形DP)
4784: [Zjoi2017]仙人掌 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 312 Solved: 181[Submit][Status] ...
- BZOJ4784 ZJOI2017仙人掌(树形dp+dfs树)
首先考虑是棵树的话怎么做.可以发现相当于在树上选择一些长度>=2的路径使其没有交,同时也就相当于用一些没有交的路径覆盖整棵树. 那么设f[i]为覆盖i子树的方案数.转移时考虑包含根的路径.注意到 ...
- 2019.02.07 bzoj4784: [Zjoi2017]仙人掌(仙人掌+树形dp)
传送门 题意:给一个无向连通图,问给它加边形成仙人掌的方案数. 思路: 先考虑给一棵树加边形成仙人掌的方案数. 这个显然可以做树形dp. fif_ifi表示把iii为根的子树加边形成仙人掌的方案数. ...
- 【BZOJ4784】[ZJOI2017]仙人掌(Tarjan,动态规划)
[BZOJ4784][ZJOI2017]仙人掌(Tarjan,动态规划) 题面 BZOJ 洛谷 题解 显然如果原图不是仙人掌就无解. 如果原图是仙人掌,显然就是把环上的边给去掉,变成若干森林连边成为仙 ...
- ●洛谷P3687 [ZJOI2017]仙人掌
题链: https://www.luogu.org/problemnew/show/P3687题解: 计数DP,树形DP. (首先对于这个图来说,如果初始就不是仙人掌,那么就直接输出0) 然后由于本来 ...
- 【做题】ZJOI2017仙人掌——组合计数
原文链接 https://www.cnblogs.com/cly-none/p/ZJOI2017cactus.html 给出一个\(n\)个点\(m\)条边的无向连通图,求有多少种加边方案,使得加完后 ...
- LOJ2250 [ZJOI2017] 仙人掌【树形DP】【DFS树】
题目分析: 不难注意到仙人掌边可以删掉.在森林中考虑树形DP. 题目中说边不能重复,但我们可以在结束后没覆盖的边覆盖一个重复边,不改变方案数. 接着将所有的边接到当前点,然后每两个方案可以任意拼接.然 ...
- zjoi2017 仙人掌
题解: 好难的dp啊...看题解看了好久才看懂 http://blog.csdn.net/akak__ii/article/details/65935711 如果一开始的图就不是仙人掌,答案显然为0, ...
- 【题解】ZJOI2017仙人掌
感觉这题很厉害啊,虽然想了一天多但还是失败了……(:д:) 这题首先注意到给定图中如果存在环其实对于答案是没有影响的.然后关键之处就在于两个 \(dp\) 数组,其中 \(f[u]\) 表示以 \(u ...
随机推荐
- webpack和webpack-dev-server安装配置(遇到各种问题的解决方法)
跟着Webpack傻瓜式指南(一)这个教程在安装webpack和webpack-dev-server的时候遇到很多问题,查了很多终于一一找到解决办法. 主要参考了这三篇博文: moudule.js:3 ...
- Spring——scope详解(转载)
摘自<spring 解密> scope用来声明IOC容器中的对象应该处的限定场景或者说该对象的存活空间,即在IOC容器在 对象进入相应的scope之前,生成并装配这些对象,在该对象不再处于 ...
- SQL SERVER 执行大于80M的SQL 脚本
在CMD控制器窗口 使用SqlCmd命令来执行 具体请看 SqlCmd /? 或者MSDN
- C各个类型的大小
1个字节(byte)是8bit. 我采用的是64位系统,64位指CPU寄存器的数据宽度是64位的. short 和 int:short比int更节省空间,short占内存是Int的一半,当要考虑程序的 ...
- Android开发遇到短信备份失败
今天做了一个有关ContentProvider的短信备份的小案例,遇到短信备份失败,费了一番周折后终于找到了问题所在 该案例是将短信写到一个xml文件然后保存在手机存储中实现短信的备份功能,关键实现代 ...
- css颜色代码对照
FFFFFF #DDDDDD #AAAAAA #888888 #666666 #444444 #000000 #FFB7DD #FF88C2 #FF44AA #FF0088 #C10066 #A ...
- MYSQL数据库-约束
约束是一种限制,它通过对表的行或列的数据做出限制,来确保表的数据的完整性.唯一性. MYSQL中,常用的几种约束: 约束类型: 主键 默认值 唯一 外键 非空 关键字: PRIMARY KEY DEF ...
- Java常量笔记
在添加文件名的同时,文件名和内容可以不相同!! 1·Java 常量 常量就是固定不变的量,一旦被定义,它的值就不能被改变. 例实: 书中的代码不全,在这里不补充一下: 书中的源代码: public c ...
- js前端获取页面传递的参数
转载的,先记下: UrlParm = function () { // url参数 var data, index; (function init() { data = []; index = {}; ...
- 【Egret】使用Egret开发的HTML5项目,使用本地电脑作为服务器进行移动端调试流程
流程: 1.先开启本机IIS服务器 具体开启流程如下:http://www.jb51.net/article/29787.htm 2.添加服务器MIME类型, 按照以下两个教程把所有的MIME类型都添 ...