题链:

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冲刺No.8

    一.站立式会议 解决真实手机中出现的各种问题 细化界面设计 数据库上传与获取日拍 二.项目实际进展 能够上传和获取日拍信息 界面设计微调 三.燃尽图 四.团队合照 五.总结 白天金工实习,晚上才有时间 ...

  2. python的Collections 模块

    Collections 模块 知识点 Counter 类 defaultdict 类 namedtuple 类 在这个实验我们会学习 Collections 模块.这个模块实现了一些很好的数据结构,它 ...

  3. Tornado 协程

    同步异步I/O客户端 from tornado.httpclient import HTTPClient,AsyncHTTPClient def ssync_visit(): http_client ...

  4. 201421123042 《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 接口 Comparable Arrays.sort -has a Lambda表达式 1.2尝试使用思维导图将这些关键词组织起来 ...

  5. HTML事件处理程序

    事件处理程序中的代码执行时,有权访问全局作用域中任何代码. //为按钮btn_event添加了两个个事件处理程序,而且该事件会在冒泡阶段触发(最后一个参数是false). var btn_event ...

  6. jupyter notebook下python2和python3共存(Ubuntu)

    提示NOTICE 时间:2018/04/06 主题:Ubuntu 下CAFFE框架 主角:Jupyter Notebook 简介: Jupyter Notebook(此前被称为 IPython not ...

  7. 用python实现与小米网关通讯

    python 与小米网关通讯的三块内容: 以下内容的理解需要配合<绿米网关局域网通讯协议>使用 1.监听网关发出的组播信息:(有网关及连接设备的生命信号,事件信息) 2.读取需要获得的信息 ...

  8. NodeJs实现自定义分享功能,获取微信授权+用户信息

    最近公司搞了个转盘抽奖的运营活动,入口放在了微信公众号里,好久没碰过微信了,刚拾起来瞬间感觉有点懵逼....似乎把之前的坑又都重新踩了一遍,虽然过程曲折,不过好在顺利完成了,而且印象也更加深刻了,抽时 ...

  9. git常用命令行总结

    Git是当今最流行的版本控制工具.这几年GitHub也干掉了GoogleCode和Sourceforge,从三大代码仓库中脱颖而出,除了GitHub自身的优秀外,Git也是功不可没. 为何Git如此出 ...

  10. 用于水和水蒸汽物性计算的Python模块——iapws

    无论是火电还是核电,将能量转化为电能的方式主要还是烧开水,即加热水产生高压蒸汽驱动汽轮机做功再发电.在进行热力循环分析.流动传热计算时,需获得水和水蒸汽的物性参数.网上主流的水蒸汽物性计算程序是上海成 ...