仙人掌

Description

如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌。所谓简单环即不经过重复的结点的环。


现在九条可怜手上有一张无自环无重边的无向连通图,但是她觉得这张图中的边数太少了,所以她想要在图上连上一些新的边。同时为了方便的存储这张无向图,图中的边数又不能太多。经过权衡,她想要加边后得到的图为一棵仙人掌。

不难发现合法的加边方案有很多,可怜想要知道总共有多少不同的加边方案。两个加边方案是不同的当且仅当一个方案中存在一条另一个方案中没有的边。

Input

多组数据,第一行输入一个整数T表示数据组数。
每组数据第一行输入两个整数n,m,表示图中的点数与边数。
接下来m行,每行两个整数u,v(1≤u,v≤n,u!=v)表示图中的一条边。保证输入的图联通且没有自环与重边。
Sigma(n)<=5*10^5,m<=10^6,1<=m<=n*(n-1)/2

Output

对于每组数据,输出一个整数表示方案数,当然方案数可能很大,请对998244353取模后
输出。

Sample Input

2
3 2
1 2
1 3
5 4
1 2
2 3
2 4
1 5

Sample Output

2
8

对于第一组样例合法加边的方案有 {}, {(2,3)},共 2 种。


咱可是来学仙人掌的啊……
然后被标题骗进来了……
不过这还真是一道很棒的题呢~
如果是咱这种蒟蒻的话肯定是想不出来的呢~


思路:

让咱来全真模拟一下这道题应有的思路过程吧~

首先,看题发现完全不可做,决定水部分分。
于是我们把目光投向数据范围——
咦居然有为树的部分分,这么良心???

那么树咱该怎么做呢?
很显然,说到计数绝大多数情况都是DP,既然是树那就树形DP呗~
那么就是用树形DP处理仙人掌上每条边被覆盖的情况了~

然而这时发生了一个问题:
因为仙人掌上会有不存在与任一个环上的边,也就是说总共被覆盖的边数不确定,如果把这种情况算进去,就会变得很难表示状态不是吗?

通过观察题面咱得到了一个解法:
题面不是说没有重边吗?
那么我们把仙人掌上不属于任意一个环的边当做被自己的一条重边覆盖就好了。
就像这样:

 0
/ \
\ /0

这样就能使总共被覆盖的边数永远等于m了~

那就来DP吧~
令f[x]表示以x为根节点的子树中连好边的情况数。
那么分两种情况:

1.子树中没有向上连出子树的边(也就是根节点)
把所有直接相连的子节点处的答案乘起来,再乘上所有直接相连的子节点的子树之间两两配对的方案数即可。

2.子树中有一条向上连出的边(啥你说为什么只有一条?这特么可是棵树啊)
那么就是说有一条神奇的边并没有与其他的当前子树中的节点相连而是连出去了。
枚举这条边并对所得结果求和,咱可以发现一个特点:
这种情况可以表示为在上面的情况的基础上加入当前正在枚举的点与其子节点的子树匹配,能与当前正在枚举的点匹配就代表是连出去了。

于是咱先预处理出一个g[x]数组,表示x个节点之间两两配对的方案数。
得到一个递推式:

g[i]=g[i−1]+g[i−2]∗(i−1)g[i]=g[i-1]+g[i-2]*(i-1)

也就是说,加入一个新节点的方案数,等同于原有的i-1个节点正常匹配,当前加入节点不参与匹配,或者在原有i-2个节点中,有一个节点被挑选出来与新加入的节点匹配,其余i-2个正常匹配的方案数的和。

然后是求f[x]数组,这显然就是一个普通的树形DP了,初始为1,乘上每个直接相连的子节点的f值,最后如果是第一种情况就乘上下标为子节点数的g值,第二种就乘上下标为子节点数+1的g值,就可以了~

所以扯了这么久部分分有什么卯月啊……
当然有!
发现不是仙人掌的情况,输出0。
其他情况下,已有的环显然毫无意义——因为咱加边时根本不能碰它们,不然就不是一棵仙人掌了~
那么直接选择性无视这些环,把原图变成一个森林,对每棵树用部分分方法求解即可~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>using namespace std;typedef long long ll;inline int read()
{int x=0;char ch=getchar();while(ch<'0' || '9'<ch)ch=getchar();while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();return x;
}const int N=5e5+9;
const int M=1e6+9;
const int md=998244353;int to[M],nxt[M],beg[N],tot;
int n,m,fa[N],dep[N],dfn[N],path[N],a[N],dfs_clock;
ll f[N],g[N],ans;inline void adde(int u,int v)
{to[++tot]=v;nxt[tot]=beg[u];beg[u]=tot;
}inline void add(int u,int v)
{adde(u,v),adde(v,u);
}void dfs(int u)
{dfn[u]=++dfs_clock;for(int i=beg[u],v;i;i=nxt[i])if((v=to[i])!=fa[u] && !dfn[v]){fa[v]=u;dep[v]=dep[u]+1;dfs(v);}
}inline bool cmp(int a,int b)
{return dep[a]<dep[b];
}void dfs2(int u,bool isrt)
{f[u]=1;path[u]=-1;int soncnt=0;for(int i=beg[u],v;i;i=nxt[i])if((v=to[i])!=fa[u] && path[v]==1){soncnt++;dfs2(v,0);(f[u]*=f[v])%=md;}if(soncnt==0)return;if(isrt)(f[u]*=g[soncnt])%=md;else(f[u]*=g[soncnt+1])%=md;
}int main()
{g[0]=g[1]=1;for(int i=2;i<N;i++)g[i]=(g[i-1]+g[i-2]*(i-1))%md;int T=read();while(T--){n=read();m=read();tot=1;for(int i=1;i<=n;i++)beg[i]=fa[i]=dfn[i]=dep[i]=path[i]=0;for(int i=1;i<=m;i++)add(read(),read());tot=0;dep[1]=1;fa[1]=0;dfs(1);for(int i=1;i<=m;i++){int st=to[i<<1],ed=to[i<<1|1];if(dfn[st]<dfn[ed])swap(st,ed);while(st!=ed){path[st]++;if(path[st]>2)goto wa;st=fa[st];}}if(0){wa:;puts("0");continue;}for(int i=1;i<=n;i++)a[i]=i;sort(a+1,a+n+1,cmp);ans=1ll;for(int i=1;i<=n;i++)if(path[a[i]]!=-1){dfs2(a[i],1);(ans*=f[a[i]])%=md;}printf("%lld\n",ans);}return 0;
}


[BZOJ4784][UOJ290][ZJOI017]仙人掌-树形DP相关推荐

  1. bzoj4316: 小C的独立集(仙人掌+树形dp)

    传送门 题意:给出一个仙人掌森林求其最大独立集. 思路:如果没有环可以用经典的树形 d p dp dp解决. f i , 0 / 1 f_{i,0/1} fi,0/1​表示第 i i i个点不选/选的 ...

  2. 2019.02.07 bzoj4316: 小C的独立集(仙人掌+树形dp)

    传送门 题意:给出一个仙人掌森林求其最大独立集. 思路:如果没有环可以用经典的树形dpdpdp解决. fi,0/1f_{i,0/1}fi,0/1​表示第iii个点不选/选的最大独立集. 然后fi,0+ ...

  3. BZOJ 4316: 小C的独立集 仙人掌 + 树形DP

    4316: 小C的独立集 Time Limit: 10 Sec  Memory Limit: 128 MB Description 图论小王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨. ...

  4. bzoj 4316: 小C的独立集(仙人掌树形DP)

    4316: 小C的独立集 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 682  Solved: 380 [Submit][Status][Disc ...

  5. cactus仙人掌图【仙人掌圆方树+树形DP+单调队列】

    题目链接 BZOJ 1023 首先,圆方树是比较好想到的,维护直径,我们最方便的做法就是先让它变成一棵树,这里因为是仙人掌图,所以就用圆方树来构建. 再者,就是维护直径了,比较好想到的是非环上结点,就 ...

  6. [XSY] 绿色(圆方树、树形DP、树上差分)

    绿色 题意简述 题解 首先,每次修改完点权后,重新考虑一遍所有路径显然是不现实的,所以我们考虑求出经过每个点的两端同色的简单路径数,这样权值和容易统计和修改. 接下来分析仙人掌上的简单路径性质.一条简 ...

  7. BZOJ.1023.[SHOI2008]cactus仙人掌图(DP)

    题目链接 类似求树的直径,可以用(类似)树形DP求每个点其子树(在仙人掌上就是诱导子图)最长链.次长链,用每个点子节点不同子树的 max{最长链}+max{次长链} 更新答案.(不需要存次长链,求解过 ...

  8. BNUOJ 52305 Around the World 树形dp

    题目链接: https://www.bnuoj.com/v3/problem_show.php?pid=52305 Around the World Time Limit: 20000msMemory ...

  9. [树形dp] Jzoj P5233 概率博弈

    Description 小A和小B在玩游戏.这个游戏是这样的: 有一棵n个点的以1为根的有根树,叶子有权值.假设有m个叶子,那么树上每个叶子的权值序列就是一个1->m 的排列. 一开始在1号点有 ...

最新文章

  1. 【CV】带你入门多目标跟踪(一)领域概述
  2. java runtime类高级,java入门 -- RunTime类
  3. 领域应用 | 企业效益最大化的秘密:知识图谱
  4. iOS 的 XMPPFramework 简介
  5. 数据科学入门的5个技巧
  6. word加水印铺满java,Word 2010文档中让水印铺满整个页面的设置方法
  7. 【luogu P4114 Qtree1】 题解
  8. 算法笔记二分查找问题1
  9. http缓存与cdn相关技术
  10. U盘无法打开的解决方法大全
  11. 抑郁焦虑测试软件可信度,做题自测抑郁症可靠吗
  12. linux est 时间,Linux中的时区设置
  13. 你还在打王者荣耀吗?用pygame写个游戏自己玩吧!
  14. 《TCP/IP详解》中文版下载地址
  15. FFmpeg 工具:音视频开发都用它,快@你兄弟来看丨音视频工具
  16. 无法往开启kerberos的zookeeper上注册服务_英雄联盟手游拳头账号有几个区?lol手游韩国账号怎么注册?...
  17. 算法之最长公共子序列问题
  18. 决策树算法分析天气、周末和促销活动对销量的影响
  19. 四通OKI 5860SP打印机故障的经验分析
  20. 超声波驱虫器,把蚊蟑鼠蚁全赶跑

热门文章

  1. mx250 计算能力_mx250显卡性能怎么样,mx250显卡性能相当于GTX多少
  2. Thinkphp5添加谷歌双重验证
  3. 实习每日总结_20161219
  4. 敏捷开发Scrum学习
  5. DNS:快速搭建公司内网DNS解析服务器
  6. matlab 导出图片坐标_Matlab绘图---坐标系显示设置
  7. SylixOS ECS 启动过程
  8. mac 重启php-fpm
  9. Java Spring MVC框架 VIII 之 Spring MVC拦截器
  10. vue 所有dom加载完毕后操作dom节点