4784: [Zjoi2017]仙人掌

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 312  Solved: 181
[Submit][Status][Discuss]

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 种。

HINT

Source

ZJOI2017 DAY1的题目质量相当高啊,都是比较自然清新的思路加上非毒瘤的代码,做起来真是一种享受。

由于以前没有接触过仙人掌DP,所以这里要有一个清楚的认识。

首先我们知道环套树DP,就是基环外向树类型的题目,大致就是先找到基环,然后对每棵树DP,最后枚举将环上的每一条边断开,具体见BZOJ1040骑士。

现在我们来看仙人掌图。仙人掌图的定义是每条边最多在一个简单环中,仔细分析可知,实际上就是环和树的拼接,如果将所有环拿走的话会发现,整幅图会变成一个森林。

那么我们就可以从这个方向考虑这个问题了。第一个问题是,如何判断一个图是不是仙人掌图。首先建出DFS树,然后对于每条反祖边,将整个环上的边标记,如果有边被标记超过两次则说明不是仙人掌图。具体可以看下面的代码,这里还有一种方法:用树上差分实现标记。

 void _dfs(int x,int f) {
vi[x]=;
dep[x]=dep[f]+;
RAL(i,x) if(e[i].to!=f) {
if(!vi[e[i].to]) _dfs(e[i].to,x);
else if(dep[e[i].to]<dep[x]) {
bt[x]++;bt[e[i].to]--;
}
}
} int fl;
void _gatherS(int x) {
RAL(i,x) if(dep[x]+==dep[e[i].to]) {
_gatherS(e[i].to);bt[x]+=bt[e[i].to];
if(!bt[e[i].to]) bi[i]=bi[i^]=;
} if(bt[x]>) fl=;
}

现在考虑如何DP,首先我们知道我们不可能在环上加边,所以我们忽略掉环边,这题就成功转化为了树形DP。然后对于每棵树求出可以加边的方案数,这个就是常规的DP。具体可以看:

https://www.cnblogs.com/wfj2048/p/6636028.html

这样,问题就轻松解决了。思路非常清晰而巧妙,确实是一道好题。

 #include<cstdio>
#include<algorithm>
#include<cstring>
#define rep(i,l,r) for (int i=l; i<=r; i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=,md=;
struct D{ int id,d; }a[N];
int h[N],fa[N],dfn[N],lu[N],dep[N],to[N<<],nxt[N<<],n,m,u,v,cnt,T,nd;
ll f[N],g[N],ans;
bool cmp(const D &a,const D &b){ return a.d<b.d; } void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int x,int p){
fa[x]=p; dfn[x]=++nd; dep[x]=dep[p]+;
For(i,x) if (!dfn[k=to[i]]) dfs(k,x);
} void dp(int x,int rt){
lu[x]=-; f[x]=; int tot=;
For(i,x) if ((k=to[i])!=fa[x] && lu[k]==) tot++,dp(k,),f[x]=f[x]*f[k]%md;
if (!rt) f[x]=f[x]*g[tot+]%md; else f[x]=f[x]*g[tot]%md;
} void work(){
scanf("%d%d",&n,&m); cnt=;
rep(i,,n) lu[i]=fa[i]=dep[i]=dfn[i]=h[i]=;
rep(i,,m) scanf("%d%d",&u,&v),add(u,v),add(v,u);
nd=; dfs(,);
rep(i,,m){
int u=to[i<<],v=to[(i<<)|];
if (dfn[u]<dfn[v]) swap(u,v);
while (u!=v){
if (lu[u]==){ printf("0\n"); return; }
lu[u]++; u=fa[u];
}
}
rep(i,,n) a[i].id=i,a[i].d=dep[i];
sort(a+,a+n+,cmp); ans=;
rep(i,,n){
int x=a[i].id; if (lu[x]==-) continue;
dp(x,); ans=ans*f[x]%md;
}
printf("%lld\n",ans);
} int main(){
g[]=g[]=; rep(i,,) g[i]=(g[i-]+(i-)*g[i-])%md;
for (scanf("%d",&T); T--; ) work();
return ;
}

[BZOJ4784][ZJOI2017]仙人掌(树形DP)的更多相关文章

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

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

  2. uoj#290. 【ZJOI2017】仙人掌(数数+仙人掌+树形dp)

    传送门 这图可以说是非常形象了2333 模拟赛的时候打了个表发现为一条链的时候答案是\(2^{n-2}\)竟然顺便过了第一个点 然后之后订正的时候强联通分量打错了调了一个上午 首先不难发现我们可以去掉 ...

  3. bzoj4784 [Zjoi2017]仙人掌

    Description 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环即不经过重复的结点的环. 现在九条可怜手上有一张无自环无重边的无向连通图,但是她觉得 ...

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

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

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

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

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

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

  7. 2019.02.07 bzoj1487: [HNOI2009]无归岛(仙人掌+树形dp)

    传送门 人脑转化条件过后的题意简述:给你一个仙人掌求最大带权独立集. 思路:跟这题没啥变化好吗?再写一遍加深记忆吧. 就是把每个环提出来分别枚举环在图中的最高点选还是不选分别dpdpdp一下即可,时间 ...

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

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

  9. bzoj 4784: [Zjoi2017]仙人掌【tarjan+树形dp】

    其实挺简单的但是没想出来---- 首先判断无解情况,即,一开始的图就不是仙人掌,使用tarjan判断如果一个点dfs下去有超过一个点比他早,则说明存在非简单环. 然后考虑dp,显然原图中已经属于某个简 ...

随机推荐

  1. 使sqoop能够启用压缩的一些配置

    在使用sqoop 将数据库表中数据导入至hdfs时 配置启用压缩 hadoop 的命令    检查本地库支持哪些  bin/hadoop checknative 需要配置native    要编译版本 ...

  2. c++刷题(3/100)数独,栈和队列

    stack的基本操作 • s.size():返回栈中的元素数量 • s.empty():判断栈是否为空,返回true或false • s.push(元素):返回对栈顶部“元素”的可变(可修改)引用 • ...

  3. xv6/sh.c

    // Shell. #include "types.h" #include "user.h" #include "fcntl.h" // P ...

  4. Ubuntu 10.04 分辨率调整

    最近学长们看了我的本本都在问我,显卡驱动是不是出现什么问题了···分辨率这么差.当时我的分辨率是1024X768,于是我就想修改我的屏幕分辨率改成1280X800.本来很简单的事情,我做起来却非常的曲 ...

  5. maven工程的建立

    /* 我曾经接触过一个Java web项目,在进行部署时,发现这个项目涉及了maven 没有接触过maven项目的我,发现了如果需要导入maven工程,需要先在eclipse里面对maven进行配置, ...

  6. Tutorial 4: Authentication & Permissions

    转载自:http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/ Tutorial 4: Auth ...

  7. Linux系统调优及安全设置

    1.关闭SELinux #临时关闭 setenforce 0 #永久关闭 vim /etc/selinux/config SELINUX=disabled 2.设定运行级别为3 #设定运行级别 vim ...

  8. django入门--django-blog-zinnia搭建个人博客

    1.安装python 选择合适python2.7及以上版本安装https://www.python.org/downloads/ 2.建立虚拟环境 这不是必须的,但是建议使用,为每个项目单独引入依赖, ...

  9. csu 1801(合数分解+排列组合)

    1801: Mr. S’s Romance Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 15  Solved: 5[Submit][Status][W ...

  10. C语言小程序之整除

    看到有人要求用C语言写这样一个小程序,就拿来温习一下 需求:输出从1到2015这2015个自然数中,能被4或5整除,但不能被30整除的数,并计算有多少个数.   #include<stdio.h ...