题链:

https://www.luogu.org/problemnew/show/P3687
题解:

计数DP,树形DP。

(首先对于这个图来说,如果初始就不是仙人掌,那么就直接输出0)
然后由于本来图中就存在于环中的边,不可能再次被包含,
所以图中的环就把这个图分为的若干颗树。
那么答案就是分别求出每颗树的方案数并相乘。
现在问题变为了求:把一颗树通过连边使得仍然是仙人掌的方案数。
定义如下3个数组:
f[u]:表示u这颗子树中没有一条从u到子树内某个的节点的路径可以向其它子树连边的方案数。
g[u]:表示u这颗子树中有一条从u到子树内某个节点的路径可以其它子树连边的方案数。
以及一个预处理的h[n]:
表示有n个元素,每个元素可以选择另一个元素与其两两匹配或者该元素不与任何元素匹配的方案数。
先看看h[n]的求法:
h[n]=h[n-1]+h[n-2]*(n-1)
即表明第n个元素要么独立,要么与另外(n-1)个元素中的某一个匹配。
然后看看怎样DP:
对于当前的子树的根u,v是其儿子节点,num是其儿子节点个数
$$f[u]=\sum g[v] \times h[num] $$
因为num个儿子有h[num]种匹配方案数,所以乘上h[num]
(所谓的匹配就是把g[v]中的那条路径的末端和与它匹配的那个v'的g[v']中的那条路径的末端连边)

然后还要求出当前的g[u]:
$$g[u]=f[u]+\sum g[v] \times h[num-1] \times num$$
含义如下:
由于u点自己可以成为g[u]中的那条路径的末端,所以$$+f[u]$$
然后u还可以选择一个儿子g[son]中的路径来连上自己形成新的g[u]中的路径。
这样的话,方案数是 $g[son]* \sum_{v!=son} g[v] \times h[num-1] = \sum g[v] \times h[num-1]$
又因为u有num个儿子,即有num中选法,所以:
$$+\sum g[v] \times h[num-1] \times num $$

代码:

#include<bits/stdc++.h>
#define MAXN 500005
#define MOD 998244353
#define rint register int
using namespace std;
int Case,N,M,dnt,cactus,ANS;
int dfn[MAXN],lu[MAXN],fa[MAXN],f[MAXN],g[MAXN],h[MAXN];
struct node{
int id,odr;
bool operator < (const node &rtm) const{
return odr<rtm.odr;
}
}A[MAXN];
struct Edge{
int ent;
int to[MAXN*4],nxt[MAXN*4],head[MAXN];
void Reset(int n){
for(rint i=1;i<=n;i++) head[i]=0; ent=2;
}
void Adde(int u,int v){
to[ent]=v; nxt[ent]=head[u]; head[u]=ent++;
}
}E;
void dfs(int u,int dad){
dfn[u]=++dnt; fa[u]=dad;
for(int e=E.head[u];e;e=E.nxt[e]){
int v=E.to[e];
if(dfn[v]) continue;
dfs(v,u);
}
}
void DP(int u,int rt){
lu[u]=-1; f[u]=1; int num=0,gson=1;
for(int e=E.head[u];e;e=E.nxt[e]){
int v=E.to[e];
if(lu[v]!=1||v==fa[u]) continue;
DP(v,0); num++;
gson=1ll*gson*g[v]%MOD;
}
f[u]=1ll*gson*h[num]%MOD;
g[u]=(1ll*f[u]+1ll*gson*h[num-1]%MOD*num)%MOD;
}
int main(){
h[0]=h[1]=1;
for(int i=1;i<=500000;i++) h[i]=(1ll*h[i-1]+1ll*h[i-2]*(i-1))%MOD;
for(scanf("%d",&Case);Case;Case--){
scanf("%d%d",&N,&M);
E.Reset(N); dnt=0; cactus=1; ANS=1;
for(rint i=1;i<=N;i++) lu[i]=dfn[i]=fa[i]=0;
for(int i=1,a,b;i<=M;i++)
scanf("%d%d",&a,&b),E.Adde(a,b),E.Adde(b,a);
dfs(1,0);
for(int e=1,u,v;e<=M;e++){
u=E.to[e*2]; v=E.to[e*2+1];
if(dfn[u]>dfn[v]) swap(u,v);
while(v!=u){
lu[v]++;
if(lu[v]>2){cactus=0;break;}
v=fa[v];
}
}
if(!cactus){printf("%d\n",0); continue;}
for(int i=1;i<=N;i++) A[i]=(node){i,dfn[i]};
sort(A+1,A+N+1);
for(int i=1,u;i<=N;i++){
u=A[i].id;
if(lu[u]==-1) continue;
DP(u,1); ANS=1ll*ANS*f[u]%MOD;
}
printf("%d\n",ANS);
}
return 0;
}

  

●洛谷P3687 [ZJOI2017]仙人掌的更多相关文章

  1. 洛谷 P4244 [SHOI2008]仙人掌图 II 解题报告

    P4244 [SHOI2008]仙人掌图 II 题目背景 题目这个II是和SHOI2006的仙人掌图区分的,bzoj没有. 但是实际上还是和bzoj1023是一个题目的. 题目描述 如果某个无向连通图 ...

  2. 洛谷P4244 [SHOI2008]仙人掌图 II

    传送门 首先不考虑带环的仙人掌,如果只是一棵普通的树,可以通过dp求每棵子树中的最长链和次长链求树的直径. 那么如果dfs的时候遇到了环,应该用环上的两点挂着的最长链加上两点间的距离来更新树的直径,并 ...

  3. ●洛谷P3688 [ZJOI2017]树状数组

    题链: https://www.luogu.org/problemnew/show/P3688题解: 二维线段树. 先不看询问时l=1的特殊情况. 对于一个询问(l,r),如果要让错误的程序得到正确答 ...

  4. 2018.10.29 洛谷P4129 [SHOI2006]仙人掌(仙人掌+高精度)

    传送门 显然求出每一个环的大小. Ans=∏i(siz[i]+1)Ans=\prod_i(siz[i]+1)Ans=∏i​(siz[i]+1) 注意用高精度存答案. 代码: #include<b ...

  5. 洛谷P5211 [ZJOI2017]字符串(线段树+乱搞)

    题面 传送门 题解 为什么大佬们全都是乱搞的--莫非这就是传说中的暴力能进队,乱搞能AC-- 似乎有位大佬能有纯暴力+玄学优化\(AC\)(不算上\(uoj\)的\(Hack\)数据的话--这要是放到 ...

  6. 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)

    题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...

  7. 洛谷P3688/uoj#291. [ZJOI2017]树状数组

    传送门(uoj) 传送门(洛谷) 这里是题解以及我的卡常数历程 话说后面那几组数据莫不是lxl出的这么毒 首先不难发现这个东西把查询前缀和变成了查询后缀和,结果就是查了\([l-1,r-1]\)的区间 ...

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

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

  9. [洛谷日报第62期]Splay简易教程 (转载)

    本文发布于洛谷日报,特约作者:tiger0132 原地址 分割线下为copy的内容 [洛谷日报第62期]Splay简易教程 洛谷科技 18-10-0223:31 简介 二叉排序树(Binary Sor ...

随机推荐

  1. Alpha第七天

    Alpha第七天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...

  2. 高级软件工程2017第7次作业--C++团队项目:Beta阶段综合报告

    1.Beta阶段敏捷冲刺每日报告 Bate版敏捷冲刺报告--day0 Bate版敏捷冲刺每日报告--day1 Bate敏捷冲刺每日报告--day2 Bate敏捷冲刺每日报告--day3 Bate敏捷冲 ...

  3. Beta预备会议

    1. 讨论组长是否重选的议题和结论. 我们小组决定组长更换为林洋洋同学,他Web开发经验比较丰富,对任务的分配会更加明确,由于上一阶段中存在进度偏慢的问题,我们希望在Beta阶段通过更好的分工安排来保 ...

  4. win7 Anaconda 安装 scrapy模块

    之前用了很多方法,都安装不成功,今天终于成功了..说下方法.. anaconda的清华镜像:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ ...

  5. equalsignorecase 和equals的区别

    equals方法来自于Object类equalsIgnoreCase方法来自String类equals对象参数是Object 用于比较两个对象是否相等equals在Object类中方法默然比较对象内存 ...

  6. python pdb 调试

    命令行 Python -m pdb xxx.py l ----> list 显示当前代码 n ----> next 向下执行一行代码 c ----> continue 继续执行代码 ...

  7. 常用的 html 标签及注意事项

    <a> 标签 用法:用于定义超链接 清除浏览器默认样式: a { text-decoration: none;/* 去除下划线 */ color: #333;/* 改变链接颜色 */ } ...

  8. node创建第一个应用

    如果我们使用PHP来编写后端的代码时,需要Apache 或者 Nginx 的HTTP 服务器,并配上 mod_php5 模块和php-cgi. 从这个角度看,整个"接收 HTTP 请求并提供 ...

  9. 《网络》:设置三个密码:通过console口连接设备,进入特权模式,登录Telnet

    软件:Cisco Packet Tracer Instructor 软件下载链接在上一篇文章中. 内容:通过设置三个密码,熟悉采用Telnet方式配置交换机的方法. 细节说明:计算机的IP地址和交换机 ...

  10. python RE模块的使用

    摘要: re模块包括操作正则表达式的函数,一些工作中都需要用到,现在说明下使用方法. 使用说明: 一,re模块下的函数:            函数             描述 compile(pa ...