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. Discrete Logging(POJ2417 + BSGS)

    题目链接:http://poj.org/problem?id=2417 题目: 题意: 求一个最小的x满足a^x==b(mod p),p为质数. 思路: BSGS板子题,推荐一篇好的BSGS和扩展BS ...

  2. Problem 2278 YYS (FZU + java大数)

    题目链接:http://acm.fzu.edu.cn/problem.php?pid=2278 题目: 题意: 有n种卡牌,每种卡牌被抽到的概率为1/n,求收齐所有卡牌的天数的期望. 思路: 易推得公 ...

  3. div遮罩实现禁用鼠标(click、hover等)事件

    这两天在帮老师做网页,今天想实现在一块区域内禁止鼠标的各种事件,本来是想在框架模板的js文件里去修改,但是改代码的时候有点凌乱...感觉应该自己把问题想复杂了. 所以想了想要是能实现在一个区域内(如: ...

  4. VC连接access

    (1)首先拷贝 c:\program files\common files\system\ado\ 目录中的 msado15.dll 文件到项目中. (2)在VC中加入DLL,具体方法如下: (3)创 ...

  5. php文件上传——php经典实例

     php文件上传——php经典实例 表单页 <html> <head> <title>文件上传</title> <meta charset='ut ...

  6. Ubuntu下安装Python3.6并在终端输入Python就能显示Python3.6

      Ubuntu17.04自带Python2.7与Python3.5.3的版本,由于Python2与Python3有着一些差距可能需要安装更新Python3的版本,并且切换默认的Python解释器. ...

  7. 集合遍历过程iterator, 添加删除元素报异常

    list  set  遍历过程中添加或者删除元素,报异常. 使用iterator 也会报异常 ConcurrentModificationException remove只能用迭代器的remove,而 ...

  8. Python股票信息抓取~

    本来想把股票的涨跌抓取出来,用汇通网的股票为例,就找了国际外汇为例. 页面里有xhr请求,并且每个xhr的url请求的 http://api.q.fx678.com/history.php?symbo ...

  9. Jmeter中的变量(三)

    变量(Variables) Jmeter中的变量(参数化)目的是为了提供改变请求变化的机制.比如登录场景,一般不能使用同一个账号做并发操作. 变量的特点 1) JMeter变量对于测试线程而言是局部变 ...

  10. Django实现文章按年月归档、点赞和评论、发送邮件

    文章归档的实现 我们在创建文章时,会在数据库中存储文章创建的时间这样的字段,一般用的都是datetime类型,记录文章创建的年月日和时分秒,所以我们直接使用文章的创建时间分类是无法实现文章的按年月归档 ...