P6790 [SNOI2020] 生成树 题解
感觉很多题解都说的不是很清楚?如何将三操作与二操作合并起来一起处理好像都没有提到。(也有可能是我太菜了,看了半天才懂)
思路
考虑这个图一定是一个广义串并联图。为什么呢?
广义串并联图的定义是不存在一个子图包含四个点且这将四个点之间的路径看作边,这四个点和路径所构成的边构成一个完全图。
显然仙人掌不可能包含一个四个点的完全图,因为四个点的完全图包含了四个有共边的环。
那考虑这个四个点的完全图删去一条边后可不可能是仙人掌。由于删去一条边最多只能删去两个环,仍然有两个环是共边的。
而这里“边”变作路径是同理的,这里不再赘述。
因此仙人掌加上一条边仍然不存在一个四个点的完全子图,也就是这个图是广义串并联图。
然后我们就可以使用广义串并联图的经典操作:删 1 度点,缩 2 度点,叠合重边。
由于最后可以删的只剩一个点,因此考虑 DP 来求总方案数。
设 \(f_x\) 表示选边 \(x\) 的方案数,\(g_x\) 表示不选边 \(x\) 的方案数。
由于我们处理的所有点的度数都小于等于 2,因此设当前点为 \(top\),与其相连的两个点为 \(u,v\),对应的两条边分别为 \(x,y\),\(u,v\) 之间的边为 \(z\)。(如果有的话)
删 1 度点
由于是生成树,每一个点都要包含,因此直接将 \(f_x\) 累乘到答案上即可。缩 2 度点
现在先假设 \(u,v\) 之间没有连边。那我们缩 2 度点就会变成在 \(u,v\) 之间新连一条边 \(z\)。
由于 \(top\) 必须被选,因此有
f_z &= f_xf_y \\
g_z&=f_xg_y+g_xf_y
\end{aligned}
\]
然后在 \(u,v\) 之间更新一下边的信息即可。
- 叠合重边
因为输入时两个点间如果有多条边,我们可以将 \(f\) 直接设为重边的数量,这根据定义是显然的,因此一开始的图中本应没有重边。
所以有重边的原因时操作二中在 \(u,v\) 之间连了一条新边 \(z'\),但是有可能 \(u,v\) 之间本身就有一条边 \(z\),因此我们要将这两条边合并起来。
由于是生成树,两条边不能都选,但是可以都不选,因此方程式比较显然了:
f_z &= f_zg_{z'}+g_zf_{z'}\\
g_z&=g_zg_{z'}
\end{aligned}
\]
code
实现的时候用 map 来维护边的信息,复杂度阈值在 map,\(O(n\log n)\)。
可以看出来 2 操作与 3 操作是一体的,只有 2 操作过后 3 操作才有可能发生,因此要放在一起处理。
注意细节比较多,需要时刻注意边的细节维护完了没有。我因为在输入的时候没有初始化 \(g\) 被卡了半个小时。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=4e5+7,p=998244353;
int n,m,f[N],g[N],idcnt=0,ans=1,deg[N];
map <int,int> mp[N];
queue <int> q;
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;if(!mp[u][v]) mp[u][v]=mp[v][u]=++idcnt,deg[u]++,deg[v]++,g[idcnt]=1; //注意初始化
f[mp[u][v]]++;
}
for(int i=1;i<=n;i++) if(deg[i]<=2) q.push(i);
while(!q.empty()){
int top=q.front();q.pop();
if(deg[top]==0) continue;
if(deg[top]==1){ //删1度点
int u,x;tie(u,x)=*mp[top].begin();
ans=ans*f[x]%p;mp[u].erase(top);mp[top].erase(u),deg[u]--,deg[top]--;
if(deg[u]<=2) q.push(u);
}
if(deg[top]==2){ //缩2度点
int u,v,x,y,z;tie(u,x)=*mp[top].begin();tie(v,y)=*next(mp[top].begin());z=mp[u][v];
int F,G;F=(f[x]*f[y])%p,G=(f[x]*g[y]+f[y]*g[x])%p;
if(!z){mp[u][v]=mp[v][u]=++idcnt;f[idcnt]=F,g[idcnt]=G;deg[u]++,deg[v]++;}
else {f[z]=(f[z]*G+F*g[z])%p;g[z]=g[z]*G%p;} // 如果 u,v 间本身就有边,那就需要叠合重边
mp[u].erase(top),mp[v].erase(top),mp[top].erase(u),mp[top].erase(v);
deg[u]--,deg[v]--,deg[top]-=2;
if(deg[u]<=2) q.push(u);if(deg[v]<=2) q.push(v);
}
}
cout<<ans<<'\n';return 0;
}
P6790 [SNOI2020] 生成树 题解的更多相关文章
- SNOI2020 部分题解
D1T1 画图可以发现,多了一条边过后的图是串并联图.(暂时不确定) 然后我们考虑把问题变成,若生成树包含一条边\(e\),则使生成树权值乘上\(a_e\),否则乘上\(b_e\),求最终的生成树权值 ...
- BZOJ2395 [Balkan 2011]Timeismoney 【最小乘积生成树】
题目链接 BZOJ2395 题意:无向图中每条边有两种权值,定义一个生成树的权值为两种权值各自的和的积 求权值最小的生成树 题解 如果我们将一个生成树的权值看做坐标,那么每一个生成树就对应一个二维平面 ...
- HDU 4786 最小生成树变形 kruscal(13成都区域赛F)
Fibonacci Tree Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- 洛谷 题解 UVA1395 【苗条的生成树 Slim Span】
[题意] 给出一个\(n(n<=100)\)个节点的的图,求最大边减最小边尽量小的生成树. [算法] \(Kruskal\) [分析] 首先把边按边权从小到大进行排序.对于一个连续的边集区间\( ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
- JSOI2016R3 瞎BB题解
题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...
- [题解]codevs1001 舒适的路线
h3 { font-family: Consolas; color: #339966 } .math { font-family: Consolas; color: gray } 题目描述 Descr ...
- 【BZOJ1002】【FJOI2007】轮状病毒(生成树计数)
1002: [FJOI2007]轮状病毒 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 1766 Solved: 946[Submit][Status ...
- UVA 1395 苗条的生成树(最小生成树+并查集)
苗条的生成树 紫书P358 这题最后坑了我20分钟,怎么想都对了啊,为什么就wa了呢,最后才发现,是并查集的编号搞错了. 题目编号从1开始,我并查集编号从0开始 = = 图论这种题真的要记住啊!!题目 ...
随机推荐
- C++ 使用MIDI库演奏《晴天》
那些在MIDI库里徘徊的十六分音符 终究没能拼成告白的主歌 我把周杰伦的<晴天>写成C++的类在每个midiEvent里埋藏故事的小黄花 调试器的断点比初恋更漫长而青春不过是一串未 ...
- [WC2018] 通道 题解
三棵树就很毒瘤了,我们一棵一棵看. 关于第一棵树的路径,经典解法就是点分治和边分治,考虑哪种更加简单. 设 \(dis1/2/3(x)\) 表示 \(x\) 在第 \(1/2/3\) 棵树中的深度(第 ...
- bin格式转safetensors
技术背景 本文主要介绍在Hugging Face上把bin格式的模型文件转为safetensors格式的模型文件,并下载到本地的方法. bin转safetensors 首先安装safetensors: ...
- 最新版go-cqhttp的sign 签名服务器搭建教程
安装go-cqhttp 传送门 自建sign签名服务器容器: 拉取镜像(只支持amd64) docker pull hansaes/unidbg-fetch-qsign:latest 启动容器 doc ...
- Spark - [01] 概述
一.Spark是什么 Spark 是一种基于内存的快速.通用.可扩展的大数据分析引擎. Apache Spark is a unified analytics engine for large-sca ...
- 执行shell脚本报错:Syntax error: word unexpected (expecting "in")
检查语法无误后,考虑是脚本文件换行符的问题. vs创建的文件默认以CRLF(0D0A)换行. 然而对于换行,windows用CRLF(0D0A)表示,linux用LF(0A)表示. 切换脚本文件换行符 ...
- echarts柱形图给X轴坐标类目添加点击事件
在项目中遇到这么个需求要在柱形图上的x轴添加点击事件,当点击对应x轴文字的时候要弹出模态框展示子图表 根据echarts的Api给图表实例绑定点击事件 myChartInstance?.on('cli ...
- npm 如何更新项目最新依赖包
NPM 是什么? Node 软件包管理器(NPM)提供了各种功能来帮助你安装和维护项目的依赖关系. 由于错误修复.新功能和其他更新,依赖关系可能会随着时间的推移而变得过时.你的项目依赖越多,就越难跟上 ...
- NumPy学习11
今天学习了NumPy线性代数 21, NumPy线性代数 numpy_test11.py : import numpy as np ''' 21, NumPy线性代数 NumPy 提供了 numpy. ...
- NumPy学习5
今天学习了11, NumPy数组元素增删改查NumPy 数组元素的增删改查操作,主要有以下方法:数组元素操作方法函数名称 描述说明resize 返回指定形状的新数组.append 将元素值添加到数组的 ...