传送门

题意:给一个无向连通图,问给它加边形成仙人掌的方案数。


思路:

先考虑给一棵树加边形成仙人掌的方案数。

这个显然可以做树形dp。

fif_ifi​表示把iii为根的子树加边形成仙人掌的方案数。

然后有两种情况:

  1. iii点没有父亲
  2. iii点有父亲

对于第一种情况即iii是树根的情况,显然fi=(∏fv)∗g∣sonp∣f_i=(\prod f_v)*g_{|son_p|}fi​=(∏fv​)∗g∣sonp​∣​,其中gig_igi​表示给iii个儿子两两配对(每个儿子可配可不配的方案数)。

对于第二种情况有可能把iii跟父亲连上的那条边拿来放进一个环里,因此把iii也看成一个允许配对的连通块即可,则fi=(∏fv)∗g∣sonp∣+1f_i=(\prod f_v)*g_{|son_p|+1}fi​=(∏fv​)∗g∣sonp​∣+1​

现在只需要预处理出ggg数组即可,我们再次用dpdpdp解决这个问题:

g0=g1=1,gi=gi−1+(i−1)gi−2,(i≥2)g_0=g_1=1,g_i=g_{i-1}+(i-1)g_{i-2},(i\ge2)g0​=g1​=1,gi​=gi−1​+(i−1)gi−2​,(i≥2)

这个递推很简单。

于是我们就成功处理出了树的情况,现在只用考虑原图怎么搞。

  1. 原图不是一个仙人掌,puts(0)puts(0)puts(0)即可
  2. 原图是一个仙人掌,我们把所有在环上面的边都删掉就成了一个森林,把每棵树的方案数统计出来乘起来就是答案。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=5e5+5,mod=998244353;
typedef long long ll;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
int n,m,dfn[N],low[N],tot=0,cnt[N],g[N],f[N],fa[N],siz;
bool vis[N],flag;
vector<int>e[N];
map<int,int>ban[N];
inline void solve(int rt,int p){
	ban[rt][p]=ban[p][rt]=1;
	while(p!=rt){
		if(++cnt[p]==2){flag=0;return;}
		ban[p][fa[p]]=ban[fa[p]][p]=1,p=fa[p];
	}
}
void tarjan(int p){
	++siz;
	if(!flag)return;
	dfn[p]=low[p]=++tot;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		if(!flag)return;
		if(!dfn[v])fa[v]=p,tarjan(v),low[p]=min(low[p],low[v]);
		else low[p]=min(low[p],low[v]);
	}
	if(!flag)return;
	for(ri i=0,v;i<e[p].size();++i)if(fa[v=e[p][i]]!=p&&dfn[p]<dfn[v])solve(p,v);
}
void dfs(int p,int fat){
	vis[p]=1;
	int du=0,mult=1;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fat||ban[p][v])continue;
		dfs(v,p),mult=mul(mult,f[v]),++du;
	}
	if(fat)f[p]=mul(mult,g[du+1]);
	else f[p]=mul(mult,g[du]);
}
int main(){
	g[0]=g[1]=1;
	for(ri i=2;i<=500000;++i)g[i]=add(g[i-1],mul(g[i-2],i-1));
	for(ri tt=read(),ans;tt;--tt){
		n=read(),m=read(),flag=1,siz=0;
		for(ri i=1;i<=n;++i)e[i].clear(),cnt[i]=dfn[i]=low[i]=vis[i]=0,ban[i].clear();
		for(ri i=1,u,v;i<=m;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
		tarjan(1),ans=1;
		if(!flag||siz<n){puts("0");continue;}
		for(ri i=1;i<=n;++i)if(!vis[i])dfs(i,0),ans=mul(ans,f[i]);
		cout<<ans<<'\n';
	}
	return 0;
}

2019.02.07 bzoj4784: [Zjoi2017]仙人掌(仙人掌+树形dp)的更多相关文章

  1. LOJ2250 [ZJOI2017] 仙人掌【树形DP】【DFS树】

    题目分析: 不难注意到仙人掌边可以删掉.在森林中考虑树形DP. 题目中说边不能重复,但我们可以在结束后没覆盖的边覆盖一个重复边,不改变方案数. 接着将所有的边接到当前点,然后每两个方案可以任意拼接.然 ...

  2. Codeforces 980F Cactus to Tree 仙人掌 Tarjan 树形dp 单调队列

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF980F.html 题目传送门 - CF980F 题意 给定一个 $n$ 个节点 $m$ 条长为 $1$ 的边 ...

  3. 2019.02.07 bzoj4316: 小C的独立集(仙人掌+树形dp)

    传送门 题意:给出一个仙人掌森林求其最大独立集. 思路:如果没有环可以用经典的树形dpdpdp解决. fi,0/1f_{i,0/1}fi,0/1​表示第iii个点不选/选的最大独立集. 然后fi,0+ ...

  4. 2019.02.07 bzoj1487: [HNOI2009]无归岛(仙人掌+树形dp)

    传送门 人脑转化条件过后的题意简述:给你一个仙人掌求最大带权独立集. 思路:跟这题没啥变化好吗?再写一遍加深记忆吧. 就是把每个环提出来分别枚举环在图中的最高点选还是不选分别dpdpdp一下即可,时间 ...

  5. BZOJ4784 ZJOI2017仙人掌(树形dp+dfs树)

    首先考虑是棵树的话怎么做.可以发现相当于在树上选择一些长度>=2的路径使其没有交,同时也就相当于用一些没有交的路径覆盖整棵树. 那么设f[i]为覆盖i子树的方案数.转移时考虑包含根的路径.注意到 ...

  6. 洛谷 5291 [十二省联考2019]希望(52分)——思路+树形DP

    题目:https://www.luogu.org/problemnew/show/P5291 考场上写了 16 分的.不过只得了 4 分. 对于一个救援范围,其中合法的点集也是一个连通块. 2n 枚举 ...

  7. 2019.02.09 bzoj4455: [Zjoi2016]小星星(容斥原理+dp)

    传送门 题意简述:给一张图和一棵树(点数都为n≤17n \le17n≤17),问有多少种给树的标号方法方法使得图中去掉多余的边之后和树一模一样. 思路: 容斥好题啊. 考虑fi,jf_{i,j}fi, ...

  8. [BZOJ4784][ZJOI2017]仙人掌(树形DP)

    4784: [Zjoi2017]仙人掌 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 312  Solved: 181[Submit][Status] ...

  9. bzoj4784 [Zjoi2017]仙人掌

    Description 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环即不经过重复的结点的环. 现在九条可怜手上有一张无自环无重边的无向连通图,但是她觉得 ...

随机推荐

  1. 51nod 1459 迷宫游戏 dijkstra模板

    链接:迷宫游戏 问题 - 51Nod  http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1459 1459 迷宫游戏  基准 ...

  2. goto,void,extern,sizeof分析

    goto: 程序的质量与goto出现的次数成反比,禁用 goto的副作用:破环了程序的结构化的顺序执行的过程,它有可能会跳过程序的应该执行的一些步骤. void: 修饰函数返回值和参数 c语言中没有定 ...

  3. Win10 安装 Anaconda3 用 Anaconda3 安装TensorFlow 1.2 (只支持python3.5)

    Win10 安装 Anaconda3 1.安装Anaconda3 选择相应的Anaconda进行安装,下载地址点击这里,下载对应系统版本的Anaconda,官网现在的版本是Anaconda 4.3.1 ...

  4. C++旅馆问题。

    有总钱数 有每房每天住需要多少钱 问最少可以住几天 最后输入的是钱数.前边输入没个住所每天多少钱 例如: 1001 1002 1003 1004 1000 -1 100 500 600 最少一天,最多 ...

  5. 先安装win7时IIS的安装

    打开“控制面板”->选择“程序”->选择“打开或关闭windows功能”->在“Internet信息服务”中勾选以下勾选框

  6. go语言使用go-sciter创建桌面应用(七) view对象常用方法,文件选择,窗口弹出,请求

    view对象的详细文档请看: https://sciter.com/docs/content/sciter/View.htm demo9.html代码如下: <!DOCTYPE html> ...

  7. JFinal Web开发学习(二)目录、架构、package设计

    package分类 config是JFinal的项目配置 controller是控制器 handler可以设置全局处理器,例如判断用户请求中是否直接请求 FreeMarker的模板文件ftl或者htm ...

  8. [z]oracle 创建job

    https://www.cnblogs.com/lijiasnong/p/3382578.html alter system enable restricted session;--创建表create ...

  9. 理解Linux中的shutdown、poweroff、halt和reboot命令

    原文  http://os.51cto.com/art/201706/541525.htm   在本篇中,我们会向你解释 shutdown.poweroff.halt 以及 reboot 命令.我们会 ...

  10. u-boot之怎么实现分区

    启动参数bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0中kernel在哪定义,为什么可以直接引用?针对这个问题展开思考最终定位到 ...