前言

谨以此题纪念我第一次参加省选时刚了5h这一题得到0分的经历

题目相关

链接

题目大意

给出仙人掌定义:如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌
给出一个图,求有多少种加边方式使得图成为一个仙人掌

数据范围

多组数据
∑n≤5∗105,∑m≤106\sum n\le5*10^5,\sum m\le10^6∑n≤5∗105,∑m≤106

题解

首先,如果这幅图本身就已经不是仙人掌了,那么输出000
如何判断呢?我们发现,建圆方树的时候,两条圆点的连边最多只会被删除一次,如果被删除了多次那么说明这幅图不是仙人掌

对于仙人掌的简单环上的边,我们一定不可以进行一条连边使得两个端点之间有一条路径经过这条边(因为这样就会出现一条边在多个简单环中的情况)
那么我们就直接把仙人掌简单环上的边直接删除,容易发现剩下的都是一些树了

对于树的情况,我们发现有两个东西不好处理:一个是最终的图中可以有边不在简单环上,二是添加的边不能有重边

我们发现这两个问题合在一起就被解决了,我们可以转化成:每条边都必须要在一个简单环上,允许有重边。容易发现,这么转化的答案不变

现在相当于是对于一棵树,求其被链覆盖的方案数

考虑树形dp

我们定义fif_ifi​为以iii节点所在子树及其通往父亲的边被链覆盖的方案数
设sonuson_usonu​为uuu节点的儿子集合,S(n)S(n)S(n)为nnn个点、每个点的度数小于等于111的图的数量
我们发现fu=S(∣sonu∣+1)∗∏v∈sonufvf_u=S(|son_u|+1)*\prod_{v\in son_u}f_vfu​=S(∣sonu​∣+1)∗v∈sonu​∏​fv​
特殊的,我们发现根节点没有父亲边,这样的话其fif_ifi​的定义不包含通往父亲的边,那么S(x)S(x)S(x)里面的xxx就是儿子数量了

我们现在考虑怎么求S(n)S(n)S(n)
我们考虑已知S(1)S(1)S(1)~S(n)S(n)S(n),怎么求S(n+1)S(n+1)S(n+1)
我们考虑第n+1n+1n+1个点
如果它不连边,那么方案数为S(n)S(n)S(n)
如果它连边,在前面nnn个点中选择一个连,那么方案数就为S(n−1)∗nS(n-1)*nS(n−1)∗n
即得到递推式S(n+1)=S(n)+n∗S(n−1)S(n+1)=S(n)+n*S(n-1)S(n+1)=S(n)+n∗S(n−1)
这样,我们就能O(n)\mathcal O(n)O(n)的完成此题

代码

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
namespace fast_IO
{const int IN_LEN=1000000,OUT_LEN=1000000;char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
#define rg register
typedef long long ll;
template<typename T>inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template<typename T>inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template<typename T>inline void print(const T x){if(x>=0)printe(x);else putchar('-'),printe(-x);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
const int maxn=500005,maxm=2000005,mod=998244353;
int T,n,m;
int head[maxn],nxt[maxm],tow[maxm],tmp;bool del[maxm],DIE,VVV[maxm],vis[maxn];
inline void addb(const int u,const int v)
{tmp++;nxt[tmp]=head[u];head[u]=tmp;tow[tmp]=v;del[tmp]=VVV[tmp>>1]=0;
}
int stack[maxn],top,H;
void dfs(const int u)
{if(DIE)return;vis[u]=1;for(rg int i=head[u];i&&!DIE;i=nxt[i])if(!VVV[i>>1]){VVV[i>>1]=1;const int v=tow[i];stack[++top]=i;if(vis[v]){H=top;for(rg int j=top-1;j&&tow[stack[j]]!=v;j--)H=j;for(rg int j=top;j>=H;j--){const int id=stack[j];if(del[id])DIE=1;del[id]=del[id^1]=1;}}else dfs(v);top--;}
}
ll S[maxn],f[maxn],ans;
void DFS(const int u,const int fa)
{int size=0;f[u]=1,vis[u]=1;for(rg int i=head[u];i;i=nxt[i])if(!del[i]){const int v=tow[i];if(v!=fa)DFS(v,u),f[u]=f[u]*f[v]%mod,size++;}if(fa)size++;f[u]=f[u]*S[size]%mod;
}
int main()
{S[0]=S[1]=1;for(rg int i=2;i<=500000;i++)S[i]=(S[i-1]+S[i-2]*(i-1))%mod;read(T);while(T--){tmp=1;read(n),read(m);top=0;for(rg int i=1;i<=m;i++){int u,v;read(u),read(v);addb(u,v),addb(v,u);}DIE=0,dfs(1);if(DIE)ans=0;else{ans=1;for(rg int i=1;i<=n;i++)vis[i]=0;for(rg int i=1;i<=n;i++)if(!vis[i]){int d=0;for(rg int j=head[i];j;j=nxt[j])if(!del[j])d++;if(d==1){DFS(i,0);ans=ans*f[i]%mod;}}}print(ans),putchar('\n');for(rg int i=1;i<=n;i++)head[i]=0,vis[i]=0;}return flush(),0;
}

总结

其实和仙人掌没啥关系,就是很清真的树形dp,被题目名劝退了
然后转化问题的思路比较巧妙
最后,祈祷今年浙江省选RP++吧

[zjoi2017]仙人掌相关推荐

  1. ●洛谷P3687 [ZJOI2017]仙人掌

    题链: https://www.luogu.org/problemnew/show/P3687 题解: 计数DP,树形DP. (首先对于这个图来说,如果初始就不是仙人掌,那么就直接输出0) 然后由于本 ...

  2. 【题解】ZJOI2017仙人掌

    感觉这题很厉害啊,虽然想了一天多但还是失败了--(:д:) 这题首先注意到给定图中如果存在环其实对于答案是没有影响的.然后关键之处就在于两个 \(dp\) 数组,其中 \(f[u]\) 表示以 \(u ...

  3. ZJOI2017 仙人掌

    题目大概是给个无向图,求添加边使其变为仙人掌的方案数. 直接判断是否仙人掌,特判输出0即可. 否则的话,把环拆开成链变成一个树,考虑暴力计算儿子的配对方案数,打表规律:\(f[i] = f[i - 1 ...

  4. BZOJ4784 [Zjoi2017]仙人掌

    标签:树形DP,tarjan,仙人掌 题目 题目传送门 Description 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环即不经过重复的结点的环. 现 ...

  5. 【ZJOI2017】仙人掌 题解

    题目大意 ~~~~~~给出一个无重边无自环的无向连通图(n 个点 m 条边),问有多少种再往上加边的方案,使得新图是仙人掌.       ~~~~~~多组数据, n<=5e5, ∑m\sum m ...

  6. 【ZJOI2017】仙人掌

    题面 https://www.luogu.org/problem/P3687 题解 如果原图不是仙人掌(在这里,我们认为一棵树也是一个仙人掌),则无解,输出$0$. 如果原图是一个仙人掌,加的边是不能 ...

  7. Forest Program dfs+tanjar仙人掌

    题目链接 CCPC2019 F题. 题意:给一颗仙人掌树,让你求每一个小环的边的个数,用快速幂即可求解. 思路:第一反应是tanjar乱搞,把每个环上的点取出来,类似于缩点的方法.但是忽然感觉dfs能 ...

  8. 基于卷积神经网络(CNN)的仙人掌图像分类

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 今天我们的目标是建立一个分类器,将图像分类为"仙人掌&q ...

  9. SHOI2008仙人掌图(tarjan+dp)

    Solution 好题啊没的说. 本题需要求出仙人掌的直径,但仙人掌是一个带有简单环的一张图无法直接用树形dp求解,但它有一个好东西就是没有类似环套环的东西,所以我们在处理时就方便了一些. 思路:ta ...

最新文章

  1. ListView之SimpleAdapter的使用
  2. Cocoa如何应用设计模式
  3. 为什么说神经网络可以逼近任意函数?
  4. 【汇编语言】DEBUG的使用
  5. 处理文件、摄像头和图形用户界面
  6. UML九种图 之 包图和对象图
  7. java洗扑克牌算法分析_IT兄弟连 Java语法教程 综合案例
  8. Python 将输出内容保存在text文件中
  9. 将JS对象转换为JSON字符串
  10. delphi如何获得select得到的信息_如何建立闭环的笔记体系
  11. ubuntu终端运行python找不到模块_在ubuntu里直接通过终端编写运行python程序
  12. 以围棋来说,人工智能程序跟通常程序差异在哪里
  13. 教你在电脑中学会视频格式批量转换
  14. 实车开放道路真体验,开启车联网发展新征程
  15. No ip domain-lookup和Logging synchronous和Exec-timeout 0 0
  16. 案例|工业物联网解决方案•工业互联网云平台
  17. 经典电影list(辛辛苦苦整理的)
  18. 马斯克让位?特斯拉中国一把手被曝将接任全球CEO,内部回应:您觉得是真的吗?...
  19. 深度学习:GPU云服务器的租用
  20. hive 已知日期计算是周几

热门文章

  1. 通过一个图来简单描述一下 socket 链接建立以及通信的模型
  2. 简单异常处理器SimpleMappingExceptionResolver
  3. 后台服务系统之Dubbo Admin的讲解
  4. 移动APP接口安全性设计
  5. 内存溢出与内存泄漏区别
  6. Shell变量作用域
  7. php 进行http请求,PHP模拟http请求的方法详解
  8. PostgreSQL 与 MySQL 常用命令对照
  9. redhat6.x_linux学习笔记
  10. Dump文件:线程dump和堆dump