Description

P3687 [ZJOI2017]仙人掌 - 洛谷 | 计算机科学教育新生态

Solution

我们先考虑只有一棵树如何处理.

仙人掌可以看做若干环的集合. 特别的, 对于一条没有环的边, 可以加上重边, 那么这个边和它的重边构成一个环.

对于树来说, 问题就可以转化为求加上若干条边, 使树上的每一条边在且仅在一个环内的方案数.

去掉加的边, 也就是说求用若干条边不相交的链将整个树覆盖的方案数.

考虑树形dp.

设 \(f_i\) 表示考虑 \(i\) 的子树与 \(i\) 连向父亲的边, 用若干条边不相交的链覆盖的方案数;

\(g_n\) 表示一个点连出 \(n\) 条边, 用若干条边不相交的链覆盖的方案数, 也就是说, 将 \(n\) 条边两两匹配或者单独留下的方案数.

考虑最后一条边是否匹配, 我们可以得出 \(g_i\) 的通项:

\[g_i = g_{i-1} + g_{i-2} \cdot (i-1)
\]

然后求 \(f_i\):

对于非根的点 \(i\), 它连出了 \(|child(i)| + 1\) 条边. 可以考虑将 \(f_j\) 连向父亲的边两两匹配或者单独留下, 根据乘法原理, 有

\[f_i = \prod_{j \in child(i)} f_j \cdot g_{|child(i)| + 1}
\]

对于\(i = rt\), 它没有连向父亲的边, 因此

\[f_i = \prod_{j \in child(i)} f_j \cdot g_{|child(i)|}
\]

答案即为 \(f_{rt}\).

最后考虑其他的图怎么做:

如果不是仙人掌, 答案为0;

如果图是仙人掌:

对于仙人掌的一个环上的两点 \(p\) 和 \(q\), 显然不能再加边使它们在环外联通. 因此, 我们可以去掉所有的环, 对于剩下的每棵树分别求出答案, 对答案相乘即可.

Code

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll; //---------------------------------------
const int nsz=5e5+50,msz=1e6+50;
const ll nmod=998244353;
int t,n,m; struct te{int t,pr,del;}edge[msz*2];
int hd[nsz],pe=1;
#define forg(p,i,v) for(int i=hd[p],v=edge[i].t;i;i=edge[i].pr,v=edge[i].t)
void adde(int f,int t){edge[++pe]=(te){t,hd[f],0};hd[f]=pe;}
void adddb(int f,int t){adde(f,t);adde(t,f);} ll g[nsz];
void init(int bnd){
g[0]=1,g[1]=1;
rep(i,2,bnd)g[i]=(g[i-1]+g[i-2]*(i-1))%nmod;
} int vis[nsz],stkp[nsz],stk[nsz],top=0;
bool solcactus(int p,int e0){
stk[++top]=e0^1,stkp[p]=top,vis[p]=1;
forg(p,i,v){
if(i==e0)continue;
if(vis[v]){//cir
if(stkp[v]>stkp[p])continue;
edge[i].del=edge[i^1].del=1;
rep(j,stkp[v]+1,stkp[p]){
if(edge[stk[j]].del)return 0;
edge[stk[j]].del=edge[stk[j]^1].del=1;
}
continue;
}
if(solcactus(v,i^1)==0)return 0;
}
--top;
return 1;
} ll dp[nsz];
void dfs(int p,int fa){
dp[p]=1,vis[p]=1;
int cnt=0;
forg(p,i,v){
if(v==fa||edge[i].del)continue;
dfs(v,p);
dp[p]=dp[p]*dp[v]%nmod;
++cnt;
}
dp[p]=dp[p]*(fa==0?g[cnt]:g[cnt+1])%nmod;
} ll sol(){
memset(vis,0,(n+10)*sizeof(int));
top=0;
if(solcactus(1,0)==0)return 0;
memset(vis,0,(n+10)*sizeof(int));
ll res=1;
rep(i,1,n){
if(vis[i]==0)dfs(i,0),res=res*dp[i]%nmod;
}
return res;
} void init1(int n){
memset(hd,0,(n+10)*sizeof(int));
pe=1;
} int main(){
ios::sync_with_stdio(0),cin.tie(0);
init(5e5+50);
cin>>t;
rep(cs,1,t){
cin>>n>>m;
init1(n);
int a,b;
rep(i,1,m)cin>>a>>b,adddb(a,b);
cout<<sol()<<'\n';
}
return 0;
}

luogu3687-[ZJOI2017] 仙人掌的更多相关文章

  1. 【BZOJ4784】[ZJOI2017]仙人掌(Tarjan,动态规划)

    [BZOJ4784][ZJOI2017]仙人掌(Tarjan,动态规划) 题面 BZOJ 洛谷 题解 显然如果原图不是仙人掌就无解. 如果原图是仙人掌,显然就是把环上的边给去掉,变成若干森林连边成为仙 ...

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

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

  3. bzoj4784 [Zjoi2017]仙人掌

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

  4. ●洛谷P3687 [ZJOI2017]仙人掌

    题链: https://www.luogu.org/problemnew/show/P3687题解: 计数DP,树形DP. (首先对于这个图来说,如果初始就不是仙人掌,那么就直接输出0) 然后由于本来 ...

  5. 【做题】ZJOI2017仙人掌——组合计数

    原文链接 https://www.cnblogs.com/cly-none/p/ZJOI2017cactus.html 给出一个\(n\)个点\(m\)条边的无向连通图,求有多少种加边方案,使得加完后 ...

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

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

  7. zjoi2017 仙人掌

    题解: 好难的dp啊...看题解看了好久才看懂 http://blog.csdn.net/akak__ii/article/details/65935711 如果一开始的图就不是仙人掌,答案显然为0, ...

  8. 2019.02.07 bzoj4784: [Zjoi2017]仙人掌(仙人掌+树形dp)

    传送门 题意:给一个无向连通图,问给它加边形成仙人掌的方案数. 思路: 先考虑给一棵树加边形成仙人掌的方案数. 这个显然可以做树形dp. fif_ifi​表示把iii为根的子树加边形成仙人掌的方案数. ...

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

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

  10. 【题解】ZJOI2017仙人掌

    感觉这题很厉害啊,虽然想了一天多但还是失败了……(:д:) 这题首先注意到给定图中如果存在环其实对于答案是没有影响的.然后关键之处就在于两个 \(dp\) 数组,其中 \(f[u]\) 表示以 \(u ...

随机推荐

  1. 基于.NetCore的Redis5.0.3(最新版)快速入门、源码解析、集群搭建与SDK使用【原创】

    1.[基础]redis能带给我们什么福利 Redis(Remote Dictionary Server)官网:https://redis.io/ Redis命令:https://redis.io/co ...

  2. 关于px,分辨率,ppi的辨析

    概述  在本篇文章的开始,我先为大家解释一下这三个名词的概念.  px全称为pixel--像素.pc及移动设备的屏幕就是通过往像素矩阵中填充颜色,从而在宏观上体现出图像.像素越小,图像越清晰.  分辨 ...

  3. Web部分

    说出Servlet的生命周期,并说出Servlet和GCI的区别. Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init方法进行Servlet的初始化,请求到达时 ...

  4. mysql存储过程 带参数 插入 操作

    今天再次添补一下小小内容,闲话不多说,直入标题. 先来看下,如何创建带参数的 存储过程(ps:本文只限mysql5及以上版本) CREATE PROCEDURE prSaveFileInfo(Tabl ...

  5. C++系列总结——多态

    前言 封装隐藏了类内部细节,通过继承加虚函数的方式,我们还可以做到隐藏类之间的差异,这就是多态(运行时多态).多态意味一个接口有多种行为,今天就来说说C++的多态是怎么实现的. 编译时多态感觉没什么好 ...

  6. Python二级-----------程序冲刺1

    1. 仅使用 Python 基本语法,即不使用任何模块,编写 Python 程序计算下列数学表达式的结果并输出,小数点后保留3位.‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪ ...

  7. 智能指针std::unique_ptr

    std::unique_ptr 1.特性 1) 任意时刻只能由一个unique_ptr指向某个对象,指针销毁时,指向的对象也会被删除(通过内置删除器,通过调用析构函数实现删除对象) 2)禁止拷贝和赋值 ...

  8. 搭建Linux虚拟服务器

    1.搭建Linux虚拟机环境安装VMware Workstation 14下载地址:https://www.cr173.com/soft/68480.html密钥:FF31K-AHZD1-H8ETZ- ...

  9. First Show

    随便写写,记录美好生活 博客的内容主要是关于java后台开发所涉及到技术栈的学习记录

  10. sql 排序函数ROW_NUMBER分页返回数据

    分页从数据库返回一张表的某些条数据 假设我需要查询 系统表 sys.all_columns中的数据,每次查询10条 第一次查询第1-10条数据 第二次查询第11-20条数据 第三次查询第21-30条数 ...