传送门

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


思路:

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

这个显然可以做树形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. stark组件之批量操作【模仿Django的admin】

    一.先看下django的admin是如何实现批量操作 首先在配置类中定义一个函数 然后我们为这个函数对象设置一个属性,这个属性主要用来显示在select标签中显示的文本内容 最后把函数对象放到一个ac ...

  2. 关于json.stringify的注意事项

    今天通过navigator.getCurrentPosition中的success回调,用json.stringify打印postion时,怎么打印都只是一个{}, 而打印postion.coords ...

  3. 31-java中知识总结:list, set, map, stack, queue

    import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Lin ...

  4. python3中的zip函数(转)

    原文地址:https://www.cnblogs.com/qqhfeng/p/5267352.html 在window,显示变量 print(x);而在linux中 print x 例如,有两个列表: ...

  5. ifconfig 运行command not found

    vi /home/lx/.bash_profile中添加 PATH=$PATH:$HOME/bin:/sbin:/bin export PATH   linux查看服务自启动  chkconfig - ...

  6. POPUP_TO_CONFIRM的使用方法

    CALL FUNCTION 'POPUP_TO_CONFIRM'       EXPORTING         TEXT_QUESTION               = '是否要打印凭证!'    ...

  7. Hadoop(二) HADOOP集群搭建(简化版)

    1.准备Linux环境    1.0先将虚拟机的网络模式选为NAT            1.1修改主机名        vi /etc/sysconfig/network               ...

  8. OSGI引入Spring DM实现对服务对象的管理

    一.异同 熟悉Spring的应该也都了解它的IOC的功能,那么对于在OSGI开发环境下,在使用IOC功能时有什么不同呢?最重要的一点就是Spring上下文对象,每个Spring-Powered Bun ...

  9. html 转化成 pdf

  10. 常用MFC宏

    最近我在用MFC开发一个智能家居监控平台的软件(用到了MSCOMM串口通信控件),当我通过在一个对话框类A中定义另一个对话框类B的对象访问B的public成员时,提示不可访问.后来经过多天的向朋友求救 ...