luoguP3687 [ZJOI2017]仙人掌 题解
时间:2020-04-10
本文章向大家介绍luoguP3687 [ZJOI2017]仙人掌 题解,主要包括luoguP3687 [ZJOI2017]仙人掌 题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
首先这题巨坑,我们来看一看题意: 给出一张简单无向连通图,求有多少种加边方案可以使得加边后这张图是一棵仙人掌。 注意这题没有保证给出的图是一个仙人掌!那么这就意味着我们还得先判断它是不是一个仙人掌。
我们需要判断它是不是一个仙人掌,然后将环上的边删去。 有一种简单的方法是用tarjan求边双连通分量,如果一条边的两个端点都在同一个连通分量里,那么这条边一定被包含在一个环里。
然后我们就能得到若干棵树了,对每棵树进行树形dp,在每个结点处考虑非树边的情况,可以选择某一个儿子往上连一条边,也可以将两个儿子配对连边。其中将儿子两两配对连边的方案数可以预处理,用递推的方法求: \(g[i]=g[i-1]+(i-1)*g[i-2]\)
然后把每棵树根处的dp值乘起来就是最终的答案了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 1000007
#define M 2000007
#define ll long long
const int inf=0x3f3f3f3f;
const ll mod=998244353;
int hd[N],pre[M],to[M],num,dfn[N],ord,low[N],cl[N],st[N],tp;
ll f[N][2],g[N];
bool vis[N],del[M],flag;
void adde(int x,int y)
{
num++;pre[num]=hd[x];hd[x]=num;to[num]=y;
}
void tarjan(int v,int fa)
{
low[v]=dfn[v]=++ord;
st[++tp]=v;
bool bl=0;
for(int i=hd[v];i;i=pre[i])
{
int u=to[i];
if(u==fa)continue;
if(!dfn[u])
{
tarjan(u,v);
if(low[u]<dfn[v])flag|=bl,bl=1;
low[v]=min(low[v],low[u]);
}
else
{
if(dfn[u]<dfn[v])flag|=bl,bl=1;
low[v]=min(low[v],dfn[u]);
}
}
if(low[v]==dfn[v])
{
while(st[tp]!=v)cl[st[tp--]]=v;
cl[st[tp--]]=v;
}
}
void dp(int v,int fa)
{
vis[v]=1;
int s=0;
ll sum=1;
for(int i=hd[v];i;i=pre[i])
{
int u=to[i];
if(u==fa||del[i])continue;
dp(u,v);s++;
sum=sum*(f[u][0]+f[u][1])%mod;
}
f[v][0]=sum*g[s]%mod;
if(s)f[v][1]=s*sum%mod*g[s-1]%mod;
else f[v][1]=0;
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("test.out","w",stdout);
int t,n,m,x,y;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
//n=100;
ord=0;
for(int i=1;i<=n;i++)hd[i]=0,vis[i]=0,dfn[i]=low[i]=cl[i]=0;
g[0]=1;g[1]=1;
for(int i=2;i<=n;i++)g[i]=(g[i-1]+(i-1)*g[i-2])%mod;
//for(int i=1;i<=10;i++)printf("%lld\n",g[i]);return 0;
num=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
adde(x,y),adde(y,x);
}
for(int i=1;i<=num;i++)del[i]=0;
flag=0;
tarjan(1,0);
if(flag)
{
printf("%d\n",0);
continue;
}
ll ans=1;
for(int v=1;v<=n;v++)
for(int i=hd[v];i;i=pre[i])
if(cl[v]==cl[to[i]])del[i]=1;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
dp(i,0);
ans=ans*f[i][0]%mod;
}
}
printf("%lld\n",ans);
}
return 0;
}
原文地址:https://www.cnblogs.com/lishuyu2003/p/12674164.html
- spring-data-mongodb之MongoTemplate 添加数据
- Cannot create a session after the response has been committed
- spring-data-mongodb之环境准备(1)
- java8 Lambda尝尝鲜
- spring-data-mongodb之批量更新操作
- spring-data-mongodb之Aggregation
- spring-data-mongodb之gridfs
- spring-data-mongodb之自增ID实现
- spring-data-mongodb之Repositor操作数据
- mongodb java操作语法
- spring-data-mongodb之查询操作
- spring mvc开发的mongodb网页版客户端
- nginx转发后后端怎么获取用户真实IP
- MongoDB aggregation $unwind
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Docker 入门到实战教程(十二)ELK+Filebeat搭建日志分析系统
- Docker 入门到实战教程(十三)Docker Compose
- 解决IDEA2020.1版本的lombok插件问题
- 工具系列 | 视频监控RTSP转HLS解决方案
- Redis系列 |(一)六种基本数据结构
- 工具系列 | Jenkins 构建伟大,无所不能
- 工具系列 | H5自定义视频播放器实现
- 前端系列 |原生JS和jQuery循环遍历函数
- 工具系列 | H5如何实现人脸识别
- 形式化分析工具(六):HLPSL Tutorial(Example3)
- CODING DevOps + Nginx-ingress 实现自动化灰度发布
- TF入门04-TF实现Word2Vec
- TF入门03-实现线性回归&逻辑回归
- TF入门02-TensorFlow Ops
- 前端|利用Verify插件实现前端图像验证码