「ZJOI2017」仙人掌

题目大意:

给定一张无向联通图,求有多少种本质不同的不加重边的加边方案使得新图是个仙人掌。

解题思路:

如果原来的图不是仙人掌,那么答案就是 \(0\) ,否则求出这个仙人掌的无向图 \(\text{dfs}\) 树,任何一条新边都不能覆盖原来已经被覆盖过的边。那么只要把已经覆盖过得边删掉,变成若干棵树的问题,答案就是每棵树的答案的乘积。

一棵树的做法有一个很厉害的转化,因为题目要求不能有重边,我们可以允许转移加重边,并且每条树边必须被覆盖。然后会发现把每一种这样的方案删去重边后,新图就是一个合法图,且和要求的方案一一对应。

那么问题就转化为除了根以外,每一棵子树都要向子树外连恰好一条边的方案数。

考虑节点 \(u\) 是根节点,那么 \(dp(u)=w(size)\prod dp(v)\) ,\(w(size)\) 为 \(size\) 个点互相连边,每个点度数 \(\leq 1\) 的方案数,\(size\) 是 \(u\) 的儿子数量。可以理解为,每个儿子的子树要么连到别的儿子的子树里,要么连到 \(u\) 。

考虑怎么求 \(w(n)\) ,直接考虑组合意义,第 \(n\) 个点要么不连边,要么在前面选另外一个点和它连边。

\[w(n)=w(n-1)+w(n-2)\times (n-1)
\]

考虑节点 \(u\) 不是根节点的情况,要选出一个点和子树外连边,分两种情况讨论。

  1. 选出的点是 \(u\) ,方案数就是 \(w(size)\)
  2. 选出的点是子树内的某个点,那么就选一个子树向外连,其它还是正常连,方案数是 \(w(size-1)\times size\)

那么总方案数就是 \(w(size)+w(size-1)\times size =w(size+1)\) 。

然后就做完了,复杂度 \(\mathcal O(n)\) 。

code

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 500005, mod = 998244353;
map<int, int> ban[N];
vector<int> g[N];
int dfn[N], sum[N], dp[N], w[N], isr[N], n, m, Index;
inline void dfs1(int u, int fa){
dfn[u] = ++Index;
for(int i = 0; i < (int) g[u].size(); i++){
int v = g[u][i];
if(v == fa) continue;
if(dfn[v]){
ban[u][v] = 1;
if(dfn[v] < dfn[u]) sum[u]++, sum[v]--;
}
else dfs1(v, u);
}
}
inline int dfs2(int u){
for(int i = 0; i < (int) g[u].size(); i++)
if(dfn[g[u][i]] > dfn[u] && !ban[u][g[u][i]]){
if(dfs2(g[u][i])) return 1;
sum[u] += sum[g[u][i]];
}
if(sum[u] > 1) return 1;
if(sum[u] == 1) isr[u] = 1; return 0;
}
inline void dfs3(int u){
int size = 0; dp[u] = 1;
for(int i = 0; i < (int) g[u].size(); i++){
int v = g[u][i];
if(isr[v] || dfn[v] < dfn[u]) continue;
size++;
dfs3(v), dp[u] = 1ll * dp[u] * dp[v] % mod;
}
if(!size) return;
if(isr[u]) dp[u] = 1ll * dp[u] * w[size] % mod;
else dp[u] = 1ll * dp[u] * w[size+1] % mod;
}
int main(){
w[0] = w[1] = 1;
for(int i = 2; i < N; i++)
w[i] = (1ll * w[i-2] * (i - 1) + w[i-1]) % mod;
int T; read(T);
while(T--){
Index = 0;
for(int i = 1; i <= n; i++)
g[i].clear(), ban[i].clear(), dp[i] = dfn[i] = isr[i] = sum[i] = 0;
read(n), read(m);
for(int i = 1, x, y; i <= m; i++){
read(x), read(y);
g[x].push_back(y), g[y].push_back(x);
}
dfs1(1, 0);
if(dfs2(1)){ puts("0"); continue; }
int res = 1;
isr[1] = 1;
for(int i = 1; i <= n; i++) if(isr[i]){
dfs3(i), res = 1ll * res * dp[i] % mod;
}
printf("%d\n", res);
}
return 0;
}

「ZJOI2017」仙人掌的更多相关文章

  1. @loj - 2250@ 「ZJOI2017」仙人掌

    目录 @题目描述@ @solution@ @accepted code@ @details@ @题目描述@ 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环 ...

  2. 「ZJOI2017」树状数组(二维线段树)

    「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...

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

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

  4. 「ZJOI2017」树状数组

    「ZJOI2017」树状数组 以下均基于模2意义下,默认\(n,m\)同阶. 熟悉树状数组的应该可以发现,这题其实是求\(l-1\)和\(r\)位置值相同的概率. 显然\(l=1\)的情况需要特盘. ...

  5. 「NOI2012」迷失游乐园

    「NOI2012」迷失游乐园 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该 ...

  6. 「CF521E」 Cycling City

    「CF521E」 Cycling City 传送门 首先你能发现这个东西一定是两个环的公共边. 最开始想的是什么如果一个点被访问过三次那它一定是公共边的某一端之类的东西,然后发现被仙人掌叉掉. 然后就 ...

  7. 「译」JUnit 5 系列:条件测试

    原文地址:http://blog.codefx.org/libraries/junit-5-conditions/ 原文日期:08, May, 2016 译文首发:Linesh 的博客:「译」JUni ...

  8. 「译」JUnit 5 系列:扩展模型(Extension Model)

    原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...

  9. JavaScript OOP 之「创建对象」

    工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程.工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题. function createPers ...

随机推荐

  1. LintCode 539: Move Zeroes

    终于下决心开始刷题了! 选择LintCode而不是LeetCode主要是因为LintCode操作更顺手,希望能够坚持下去. 还是循序渐进吧,数据结构和算法的东西很久没碰都很生疏了,先找找感觉. 这是一 ...

  2. 【BZOJ】3527: [Zjoi2014]力 FFT

    [参考]「ZJOI2014」力 - FFT by menci [算法]FFT处理卷积 [题解]将式子代入后,化为Ej=Aj-Bj. Aj=Σqi*[1/(i-j)^2],i=1~j-1. 令f(i)= ...

  3. Oracle数据库语句

    Oracle数据库语句 ORACLE支持五种类型的完整性约束 NOT NULL (非空)--防止NULL值进入指定的列,在单列基础上定义,默认情况下,ORACLE允许在任何列中有NULL值. CHEC ...

  4. 使用generatorConfig工具自动生成mybatis的实体类以及dao接口和映射文件

    1:数据准备 创建一个数据库表 CREATE TABLE `logininfo` ( `id` ) NOT NULL AUTO_INCREMENT, `username` ) DEFAULT NULL ...

  5. NYOJ 93 汉诺塔 (数学)

    题目链接 描述 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片 ...

  6. Django之组合搜索组件(二)--另附simple_tag的创建使用方法

    这次的代码为Django之组合搜索组件(一)的改版,实现的结果和(一)相同,不同的是,这次运用simple_tag方式,使.html程序简化 所以现在就开始编程吧! 首先想使用simple_tag方法 ...

  7. 数据类型的判断 --Object.prototype.toString.call(obj)精准检测对象类型

    数据类型的判断 typeof typeof返回一个表示数据类型的字符串,返回结果包括:number.boolean.string.symbol.object.undefined.function等7种 ...

  8. Problem D. Berland Railroads Gym - 101967D (思维)

    题目链接:https://cn.vjudge.net/contest/274029#problem/D 题目大意:给你0-9每个数的个数,然后让你找出最大的数,满足的条件是任意三位相连的都能被三整除. ...

  9. thinkphp对数据库的增删改查(查询构造器)

  10. sqlalchemy更新和插入操作

    def save_app_info(self): try: # update app_info print(self.dicts) data = db_session.query(App_Info). ...