@题目描述@

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

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

不难发现合法的加边方案有很多,可怜想要知道总共有多少不同的加边方案。

两个加边方案是不同的当且仅当一个方案中存在一条另一个方案中没有的边。

输入格式

多组数据,第一行输入一个整数 T 表示数据组数。

每组数据第一行输入两个整数 n, m,表示图中的点数与边数。

接下来 m 行,每行两个整数 u, v(1 <= u, v <= n, u ≠ v) 表示图中的一条边。

保证输入的图联通且没有自环与重边。

输出格式

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

样例

样例输入

2

3 2

1 2

1 3

5 4

1 2

2 3

2 4

1 5

样例输出

2

8

数据范围与提示

1 <= ∑n <= 5*10^5,1 <= ∑m <= 10^6。

@solution@

如果所有简单环都没有公共边,则一个图是仙人掌。

我们跑一遍 tarjan,将所有返祖边对应的树链标记加一。最后如果有一条边标记 > 1 则不为仙人掌。

然后初始图肯定得是一棵仙人掌。显然我们不可能跨越环连边,于是可以把所有环上的边去掉。

剩下的图变成了一棵不相交的森林。森林之间不能连边,我们只能在树上连求方案数,再把所有树的方案数相乘。

怎么求一棵树连成仙人掌的方案数?一样要求所有简单环都没有公共边,则我们相当于选出若干条没有公共边的链,求方案数。

接下来?树形 dp?貌似没有什么高效的 dp 方法。

因为没有重边,所以我们选出来的链长度 > 1。我们再把问题进一步转化:将没有被选在链中去的那些边单独作一条链,则用边不相交的链覆盖树上所有边的方案唯一对应选出若干条没有公共边的链的方案。

接着考虑每个点的贡献,设点 i 的度数为 d[i]。则我们可以将 i 连出去的边两两配对(也可以不配对),表示 i 所相邻的配对的那些边在同一条链里。

记 f[i] 表示 i 条边互相匹配的方案数,则最终答案为 f[d[i]] 之积。

f[i] 很好求,有递推式 f[i] = f[i-1] + (i-1)*f[i-2]。前者表示不匹配,后者表示选一个匹配。

@accepted code@

#include<cstdio>
#define rep(G, x) for(Graph::edge *p = G.adj[x];p;p = p->nxt)
const int MAXN = 500000;
const int MAXM = 1000000;
const int MOD = 998244353;
struct Graph{
struct edge{
int to; edge *nxt;
}edges[2*MAXM + 5], *adj[MAXN + 5], *ecnt;
void clear(int n) {
ecnt = &edges[0];
for(int i=1;i<=n;i++)
adj[i] = NULL;
}
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
}G1, G2;
int f[MAXN + 5];
void init() {
f[0] = f[1] = 1;
for(int i=2;i<=MAXN;i++)
f[i] = (f[i - 1] + 1LL * (i - 1) * f[i - 2] % MOD) % MOD;
}
bool flag;
int d[MAXN + 5], s[MAXN + 5];
int dfn[MAXN + 5], dcnt;
void clear(int n) {
G1.clear(n), G2.clear(n);
for(int i=1;i<=n;i++)
d[i] = s[i] = dfn[i] = 0;
dcnt = 0, flag = true;
}
void tarjan(int x, int fa) {
dfn[x] = (++dcnt);
rep(G1, x) {
if( p->to == fa ) continue;
if( dfn[p->to] ) {
if( dfn[p->to] < dfn[x] )
s[x]++, s[p->to]--;
}
else tarjan(p->to, x), G2.addedge(x, p->to);
}
}
int dfs(int x, int fa) {
int ret = s[x];
rep(G2, x) {
if( p->to == fa ) continue;
int del = dfs(p->to, x);
ret += del;
if( del == 0 )
d[x]++, d[p->to]++;
else if( del >= 2 )
flag = false;
}
return ret;
}
void solve() {
int n, m; scanf("%d%d", &n, &m), clear(n);
for(int i=1;i<=m;i++) {
int u, v; scanf("%d%d", &u, &v);
G1.addedge(u, v);
}
tarjan(1, 0), dfs(1, 0);
if( flag ) {
int ans = 1;
for(int i=1;i<=n;i++)
ans = 1LL * ans * f[d[i]] % MOD;
printf("%d\n", ans);
}
else puts("0");
}
int main() {
init(); int T; scanf("%d", &T);
while( T-- ) solve();
}

@details@

坚定了我对于 ZJOI 的题都是不可做的题的决心。

@loj - 2250@ 「ZJOI2017」仙人掌的更多相关文章

  1. 「ZJOI2017」仙人掌

    「ZJOI2017」仙人掌 题目大意: 给定一张无向联通图,求有多少种本质不同的不加重边的加边方案使得新图是个仙人掌. 解题思路: 如果原来的图不是仙人掌,那么答案就是 \(0\) ,否则求出这个仙人 ...

  2. Loj #2570. 「ZJOI2017」线段树

    Loj #2570. 「ZJOI2017」线段树 题目描述 线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构.优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质. 最近可怜 ...

  3. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  4. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  5. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  6. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  7. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  8. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

  9. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

随机推荐

  1. SSM11-Redis---jedis的使用方法以及缓存同步

    1. Jedis 需要把jedis依赖的jar包添加到工程中.Maven工程中需要把jedis的坐标添加到依赖. 推荐添加到服务层.E3-content-Service工程中. 1.1. 连接单机版 ...

  2. echarts--例子

    echarts使用例子: <script type="text/javascript"> option = { title : {'x': 'center','y':' ...

  3. OpenLayers使用symbolizers样式特征

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head ...

  4. 手残,盘符前边多打一个空格导致的message d:\WEB_APP_QuChongFu\file\五月.xlsx (文件名、目录名或卷标语法不正确。)

    尝试读取并解析一个excel文件,一直提示错误 但是有个原始数据,导入就没问题 对比了一下,好像也就是字母d的大小写有区别 我先把大写的D改成小写的试试,如果是大小写问题,那应该抛出异常 好吧,好像并 ...

  5. 2018-11-1-WPF-Main-thread-gets-a-deadlock-when-stylus-input-thread-is-waiting-for-the-window-to-clos...

    title author date CreateTime categories WPF Main thread gets a deadlock when stylus input thread is ...

  6. Leetcode617.Merge Two Binary Trees合并二叉树

    给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠. 你需要将他们合并为一个新的二叉树.合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 ...

  7. vue+ElementUI项目中,上传控件为必填项,上传图片后清空提示信息

    (ps:以下是我在项目中遇到得问题及解决方法,希望对你们有帮助.如果还有其他方法,可以留言,谢谢) 一个表单页面,使用element-ui中el-upload上传图片,此项为必填项,然后写了校验规则, ...

  8. CURL POST PHP

    function SendPostCurl($url,$post_data){ $curl = curl_init(); //初始化 curl_setopt($curl, CURLOPT_URL, $ ...

  9. 通过pip工具安装selenium(初次安装、升级、降级)

    1.初始安装 语法: install selenium==版本号 2.升级安装 3.降级安装 ----------------------------------------------------- ...

  10. Codeforces 113C

    题目链接 C. Double Happiness time limit per test 5 seconds memory limit per test 128 megabytes input sta ...