【题解】ZJOI2017仙人掌
感觉这题很厉害啊,虽然想了一天多但还是失败了……(;д;)
这题首先注意到给定图中如果存在环其实对于答案是没有影响的。然后关键之处就在于两个 \(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仙人掌的更多相关文章
- 【BZOJ4784】[ZJOI2017]仙人掌(Tarjan,动态规划)
[BZOJ4784][ZJOI2017]仙人掌(Tarjan,动态规划) 题面 BZOJ 洛谷 题解 显然如果原图不是仙人掌就无解. 如果原图是仙人掌,显然就是把环上的边给去掉,变成若干森林连边成为仙 ...
- [BZOJ4784][ZJOI2017]仙人掌(树形DP)
4784: [Zjoi2017]仙人掌 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 312 Solved: 181[Submit][Status] ...
- bzoj4784 [Zjoi2017]仙人掌
Description 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环即不经过重复的结点的环. 现在九条可怜手上有一张无自环无重边的无向连通图,但是她觉得 ...
- ●洛谷P3687 [ZJOI2017]仙人掌
题链: https://www.luogu.org/problemnew/show/P3687题解: 计数DP,树形DP. (首先对于这个图来说,如果初始就不是仙人掌,那么就直接输出0) 然后由于本来 ...
- zjoi2017 仙人掌
题解: 好难的dp啊...看题解看了好久才看懂 http://blog.csdn.net/akak__ii/article/details/65935711 如果一开始的图就不是仙人掌,答案显然为0, ...
- 【做题】ZJOI2017仙人掌——组合计数
原文链接 https://www.cnblogs.com/cly-none/p/ZJOI2017cactus.html 给出一个\(n\)个点\(m\)条边的无向连通图,求有多少种加边方案,使得加完后 ...
- LOJ2250 [ZJOI2017] 仙人掌【树形DP】【DFS树】
题目分析: 不难注意到仙人掌边可以删掉.在森林中考虑树形DP. 题目中说边不能重复,但我们可以在结束后没覆盖的边覆盖一个重复边,不改变方案数. 接着将所有的边接到当前点,然后每两个方案可以任意拼接.然 ...
- 2019.02.07 bzoj4784: [Zjoi2017]仙人掌(仙人掌+树形dp)
传送门 题意:给一个无向连通图,问给它加边形成仙人掌的方案数. 思路: 先考虑给一棵树加边形成仙人掌的方案数. 这个显然可以做树形dp. fif_ifi表示把iii为根的子树加边形成仙人掌的方案数. ...
- BZOJ4784 ZJOI2017仙人掌(树形dp+dfs树)
首先考虑是棵树的话怎么做.可以发现相当于在树上选择一些长度>=2的路径使其没有交,同时也就相当于用一些没有交的路径覆盖整棵树. 那么设f[i]为覆盖i子树的方案数.转移时考虑包含根的路径.注意到 ...
随机推荐
- ReentrantLock详解
ReentrantLock概述 ReentrantLock是Lock接口的实现类,可以手动的对某一段进行加锁.ReentrantLock可重入锁,具有可重入性,并且支持可中断锁.其内部对锁的控制有两种 ...
- Ubuntu装完后要做的几件事
Ubuntu装完后要做的几件事 改hosts 无论哪里,改hosts都是第一件事,没hosts咋google.没google咋活.在终端输入命令 sudo gedit /etc/hosts在# The ...
- node解析post表单信息
一共有4种解析方式 urlencoded.json.text .raw 发起请求的form表单中可以设置三种数据编码方式 application/x-www-form-urlencoded.multi ...
- python2.7入门---运算符 &案例
已经分享过变量类型的基本概念了,接下来就研究了一下运算符的基础知识.接下来我们就来看一下内容.举个简单的例子 4 +5 = 9 .例子中,4 和 5 被称为操作数,"+" ...
- 单调队列优化dp
洛谷p3800(单调队列优化DP) 题目背景 据说在红雾异变时,博丽灵梦单身前往红魔馆,用十分强硬的手段将事件解决了. 然而当时灵梦在Power达到MAX之前,不具有“上线收点”的能力,所以她想要知道 ...
- xshell怎样打印
Xshell提供用本地打印机打印终端窗口文本的功能.在Xshell打印时可以沿用终端窗口使用的字体及颜色.且在页面设置对话框可以设置打印纸的边距. 如何设置打印纸的大小和方向: 1.打开xshell ...
- T分布、卡方分布、F分布
请参考: https://www.cnblogs.com/think-and-do/p/6509239.html
- jmeter插件之VariablesFromCSV
项目需求: 由于该项目的特殊性,需要新建很多个jmx脚本,并且这些jmx的全局用户变量都一样,如果要修改的话,没法做到统一修改. 实现思路: 为了满足需求,在jemter官网找到该插件Variable ...
- 26、js阶段性复习
1.一元运算符 Operator + 可用于将变量转换为数字: <!DOCTYPE html> <html> <body> <p> typeof 操作符 ...
- 简单工具 & 杂技
图片压缩: 腾讯智图(http://zhitu.isux.us/) 手机的所有尺寸大小规范: http://screensiz.es/phone 需求: 移动端宽高一致的盒子(因为移动端屏幕宽度不一样 ...