感觉这题很厉害啊,虽然想了一天多但还是失败了……(;д;)

  这题首先注意到给定图中如果存在环其实对于答案是没有影响的。然后关键之处就在于两个 \(dp\) 数组,其中 \(f[u]\) 表示以 \(u\) 为根的子树中能构成仙人掌的方案数, 而 \( g[x] \) 则表示 \(x\) 个节点之间两两相互搭配(可以不搭配)的总方案数。转移则为:

 \(f[u] = \prod f[v] * g[tot + [u != root]]\)

  其中 \(v\) 为 \(u\) 的儿子节点,而 \(tot\) 表示 \(u\) 的总儿子个数。为什么这样做是对的呢?我也感到非常的困惑。之前自己在思考的时候其实有一个问题一直难住我:一个节点的儿子之间可以相互连边,这怎样处理?但此时我们将这些方案巧妙地连接在了一起。我们可以默认为求出来的 \(f[u]\) 中的方案数均为有一条边连向外界的方案。当这个方案匹配到另一子树的一种方案上的时候,表示这两条连向外界的边连接在了一起。若有没有匹配的,说明这条边没有连出去或连向根节点(若连向根节点且该点为儿子节点则说明没有连出去),但一样是合法的。

  非常的厉害啊~其实感觉自己现在各种知识储备都还算可以了,但就是不够大胆,不能勇敢的提出一些想法和设想。一定要努力放开自己的思维,先猜测,再证明~

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
#define mod 998244353
#define int long long
int n, m, dep[maxn], g[maxn];
int timer, dfn[maxn], f[maxn];
int fa[maxn], mark[maxn], tot;
int cnt, ans; struct edge
{
int cnp, head[maxn], to[maxn], last[maxn];
edge() { cnp = ; }
void add(int u, int v)
{
to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++;
to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++;
}
}E1; struct node
{
int id, dep;
}a[maxn]; bool cmp(node a, node b) { return a.dep < b.dep; } int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void pre()
{
g[] = g[] = ;
for(int i = ; i < maxn; ++ i) g[i] = (g[i - ] + (i - )*g[i - ]) % mod;
return;
} void Tarjan(int u)
{
dfn[u] = ++ timer;
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i]; if(dfn[v]) continue;
fa[v] = u; dep[v] = dep[u] + ; Tarjan(v);
}
return;
} void dfs(int u, int rt)
{
mark[u] = -; f[u] = ; int tot = ;
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i]; if(v == fa[u] || mark[v] != ) continue;
tot ++; dfs(v, ); f[u] = f[u] * f[v] % mod;
}
if(!rt) f[u] = f[u] * g[tot + ] % mod;
else f[u] = f[u] * g[tot] % mod;
return;
} void Work()
{
n = read(), m = read(); E1.cnp = ;
for(int i = ; i <= n; i ++) mark[i] = fa[i] = dep[i] = dfn[i] = E1.head[i] = ;
for(int i = ; i <= m; i ++)
{
int u = read(), v = read();
E1.add(u, v);
}
dep[] = ; Tarjan();
for(int i = ; i <= m; i ++)
{
int u = E1.to[i << ], v = E1.to[i << | ];
if(dfn[u] < dfn[v]) swap(u, v);
while(u != v)
{
if(mark[u] == ) { printf("0\n"); return; }
mark[u] ++; u = fa[u];
}
}
for(int i = ; i <= n; i ++) a[i].id = i, a[i].dep = dep[i];
sort(a + , a + n + , cmp); ans = ;
for(int i = ; i <= n; i ++)
{
int x = a[i].id; if(mark[x] == -) continue;
dfs(x, ); ans = ans * f[x] % mod;
}
printf("%lld\n", ans); return;
} signed main()
{
pre(); int T = read();
while(T --) Work();
return ;
}

  

【题解】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 仙人掌

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

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

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

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

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

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

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

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

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

随机推荐

  1. 初学Node.js -环境搭建

    从毕业一直到现在都是在做前端,总感觉缺少点什么,java? PHP? .Net? 框架太多了,学起来不好掌握,听说node.js挺牛的,我决定把node.js好好的学一下.首先是环境的配置,这个配置真 ...

  2. Vue简单使用,

    一些零碎的知识点: 在js中变量的声明 有三种方式: let,var, const let: 对应的是一个块级作用域 { let a = 12 } console.log(a) 这是未声明的, var ...

  3. django之多表查询

    一.创建模型 在Models创建如下模型: from django.db import models # Create your models here. # 用了OneToOneField和Fore ...

  4. unity开发c#代码

    1.摄像头跟随主角移动,并支持旋转. 开发过程中需要摄像头以一定距离跟随player,同时会进行旋转,属于一种常见的跟随方式. using UnityEngine; using System.Coll ...

  5. scrapy编写爬虫的时候出现缺少win32api

    环境:python3.6 工具:pycharm2017.3 scrapy fetch http://www.baidu.com ModuleNotFoundError: No module named ...

  6. JDBC 的使用

    使用 MariaDB,JDBC 所有操作全部使用预处理 SQL 的基本类型与 Java 类型的对应关系 CHAR(N) - String VARCHAR(N) - String BOOLEN - bo ...

  7. 通过数据库恢复SharePoint网站

           SharePoint网站一般包含很多个数据库,最主要的有3个,分别是SharePoint_Admin_Content(管理中心数据库),SharePoint_Config(配置数据库)和 ...

  8. 思杰VDI提示“The VDI is not available”

    前言:困扰已久的问题终于解决. 问题:客户反馈无法连接VDI. 解决过程:1.登录后台查看VDI状态为关机状态尝试重新启动提示如下图: 2.判断此VDI的启动盘出现问题(注:本人环境无数据盘) 3.查 ...

  9. 【Linux运维】LNMP环境配置

    安装准备: Centos7.3 MYSQL5.6 PHP5.6 NGINX1.10.3 一.安装Mysql mysql:[root@host129 src]#cd /usr/local/src/ [r ...

  10. 输出1-n的全排(递归C++)

    [问题描述] 输出1到n之间所有不重复的排列,即1到n的全排,要求所产生的任一数列不含有重复的数字. [代码展示] #include<iostream>using namespace st ...