传送门

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


思路:

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

这个显然可以做树形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. npm、webpack、vue-cli快速上手版

    node.js和npm npm的安装和更新 Node.js下载安装,npm自带的包管理工具. 查看安装版本信息: node -v 查看node.js版本信息 npm -v 查看npm版本信息 更新np ...

  2. Android 基础 (四大组件,五大存储,六大布局)

    Android四大组件: 参考:https://blog.csdn.net/shenggaofei/article/details/52450668 Android四大组件分别为activity.se ...

  3. 安装routeos

    直接开机,会看到: 选择全部安装即可,按a.i即可. 默认账号admin,默认没有密码 基本使用 可通过/ip,/interface等可以进去不同子功能模块,可进行print,add,remove等操 ...

  4. 洛谷 P1342 请柬(SPFA)

    题目描述 在电视时代,没有多少人观看戏剧表演.Malidinesia古董喜剧演员意识到这一事实,他们想宣传剧院,尤其是古色古香的喜剧片.他们已经打印请帖和所有必要的信息和计划.许多学生被雇来分发这些请 ...

  5. crm作业知识点集合[一]

    知识点1 1.当我们通过model建立一对多或者多对多的关系的时候,默认情况下,会关联所有的字段,如果我们使用djanog的forms的话,可以加一个属性,限制我这个字段只关联哪些字段,就是用下面的写 ...

  6. 使用SQL语句创建数据库2——创建多个数据库文件和多个日志文件

    在matser数据库下新建查询,输入的命令如下: USE master GOCREATE DATABASE E_MarketON PRIMARY--主文件组( NAME ='E_Market_data ...

  7. Linux下实时查看GPU状态

    参考链接: http://blog.csdn.net/yao_yao_2015/article/details/53404389 1. 显示当前GPU使用情况 Nvidia自带了一个nvidia-sm ...

  8. socketv 验证客户端链接的合法性

    一 .socketv 验证客户端链接的合法性 send()与sendall() 验证客户端 加密验证 如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加 ...

  9. PAT 1076 Wifi密码(15)(代码)

    1076 Wifi密码(15 分) 下面是微博上流传的一张照片:"各位亲爱的同学们,鉴于大家有时需要使用 wifi,又怕耽误亲们的学习,现将 wifi 密码设置为下列数学题答案:A-1:B- ...

  10. php 多进程

    php 在使用场景中一般是处理web应用,所以多进程使用不适合在web中使用,且php-fpm中pcntl_fork不能使用,所以使用场景是在cgi模式下 一个进程调用pcntl_fork函数后,系统 ...