感觉很多题解都说的不是很清楚?如何将三操作与二操作合并起来一起处理好像都没有提到。(也有可能是我太菜了,看了半天才懂)

思路

考虑这个图一定是一个广义串并联图。为什么呢?

广义串并联图的定义是不存在一个子图包含四个点且这将四个点之间的路径看作边,这四个点和路径所构成的边构成一个完全图。

显然仙人掌不可能包含一个四个点的完全图,因为四个点的完全图包含了四个有共边的环。

那考虑这个四个点的完全图删去一条边后可不可能是仙人掌。由于删去一条边最多只能删去两个环,仍然有两个环是共边的。

而这里“边”变作路径是同理的,这里不再赘述。

因此仙人掌加上一条边仍然不存在一个四个点的完全子图,也就是这个图是广义串并联图。

然后我们就可以使用广义串并联图的经典操作:删 1 度点,缩 2 度点,叠合重边。

由于最后可以删的只剩一个点,因此考虑 DP 来求总方案数。

设 \(f_x\) 表示选边 \(x\) 的方案数,\(g_x\) 表示不选边 \(x\) 的方案数。

由于我们处理的所有点的度数都小于等于 2,因此设当前点为 \(top\),与其相连的两个点为 \(u,v\),对应的两条边分别为 \(x,y\),\(u,v\) 之间的边为 \(z\)。(如果有的话)

  1. 删 1 度点

    由于是生成树,每一个点都要包含,因此直接将 \(f_x\) 累乘到答案上即可。

  2. 缩 2 度点

    现在先假设 \(u,v\) 之间没有连边。那我们缩 2 度点就会变成在 \(u,v\) 之间新连一条边 \(z\)。

    由于 \(top\) 必须被选,因此有

\[\begin{aligned}
f_z &= f_xf_y \\
g_z&=f_xg_y+g_xf_y
\end{aligned}
\]

然后在 \(u,v\) 之间更新一下边的信息即可。

  1. 叠合重边

    因为输入时两个点间如果有多条边,我们可以将 \(f\) 直接设为重边的数量,这根据定义是显然的,因此一开始的图中本应没有重边。

    所以有重边的原因时操作二中在 \(u,v\) 之间连了一条新边 \(z'\),但是有可能 \(u,v\) 之间本身就有一条边 \(z\),因此我们要将这两条边合并起来。

    由于是生成树,两条边不能都选,但是可以都不选,因此方程式比较显然了:
\[\begin{aligned}
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] 生成树 题解的更多相关文章

  1. SNOI2020 部分题解

    D1T1 画图可以发现,多了一条边过后的图是串并联图.(暂时不确定) 然后我们考虑把问题变成,若生成树包含一条边\(e\),则使生成树权值乘上\(a_e\),否则乘上\(b_e\),求最终的生成树权值 ...

  2. BZOJ2395 [Balkan 2011]Timeismoney 【最小乘积生成树】

    题目链接 BZOJ2395 题意:无向图中每条边有两种权值,定义一个生成树的权值为两种权值各自的和的积 求权值最小的生成树 题解 如果我们将一个生成树的权值看做坐标,那么每一个生成树就对应一个二维平面 ...

  3. HDU 4786 最小生成树变形 kruscal(13成都区域赛F)

    Fibonacci Tree Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  4. 洛谷 题解 UVA1395 【苗条的生成树 Slim Span】

    [题意] 给出一个\(n(n<=100)\)个节点的的图,求最大边减最小边尽量小的生成树. [算法] \(Kruskal\) [分析] 首先把边按边权从小到大进行排序.对于一个连续的边集区间\( ...

  5. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  6. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  7. JSOI2016R3 瞎BB题解

    题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...

  8. [题解]codevs1001 舒适的路线

    h3 { font-family: Consolas; color: #339966 } .math { font-family: Consolas; color: gray } 题目描述 Descr ...

  9. 【BZOJ1002】【FJOI2007】轮状病毒(生成树计数)

    1002: [FJOI2007]轮状病毒 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1766  Solved: 946[Submit][Status ...

  10. UVA 1395 苗条的生成树(最小生成树+并查集)

    苗条的生成树 紫书P358 这题最后坑了我20分钟,怎么想都对了啊,为什么就wa了呢,最后才发现,是并查集的编号搞错了. 题目编号从1开始,我并查集编号从0开始 = = 图论这种题真的要记住啊!!题目 ...

随机推荐

  1. Paxos算法:如何解决分布式系统中的共识问题?

    背景 Paxos 算法是 Leslie Lamport(莱斯利·兰伯特)在 1990 年提出了一种分布式系统 共识 算法.这也是第一个被证明完备的共识算法(前提是不存在拜占庭将军问题,也就是没有恶意节 ...

  2. N-gram基本原理

    N-gram模型是一种语言模型(Language Model,LM),语言模型是一个基于概率的判别模型,它的输入是一句话(单词的顺序序列),输出是这句话的概率,即这些单词的联合概率(joint pro ...

  3. 海康SDK报错Structure.getFieldOrder()

    就是你调用的这个结构体以及其引用的其他结构体,可能没有getFieldOrder()的方法,你只要按照顺序把他填上去就好了.比如 public static class NET_DVR_TIME ex ...

  4. Ansible - [09] 高级语法

    error 处理机制 默认 ansible 在遇到 error 会立刻停止 playbook [root@control ansible]# cat ~/ansible/error.yml --- - ...

  5. 在Vue 3中创建和使用FormData对象

    在Vue 3中创建和使用FormData对象的具体步骤如下‌: ‌创建FormData对象‌:在Vue组件中,首先需要创建一个新的FormData对象.FormData是一个内置的JavaScript ...

  6. Supac 如何修改地址界限高层点

    编辑->图层->运算 2.选择z ->填写高度 如-180阶段 3.保存

  7. 『Plotly实战指南』--架构与设计理念

    在数据科学和数据分析领域,数据可视化是理解数据和传达信息的关键环节. Python 作为最受欢迎的编程语言之一,拥有众多强大的可视化库,而 Plotly 无疑是其中的佼佼者. 本文将深入介绍 Plot ...

  8. 本地如何访问vue2 生成的dist代码

    前言 当你使用 Vue CLI 或其他构建工具构建 Vue 2 项目时,它会生成一个 dist 文件夹,这个文件夹包含了你项目的生产环境版本的静态资源文件(HTML.JavaScript 和 CSS) ...

  9. mac地址查询

    打开命令提示符窗口(cmd程序) 快捷键 win+r 打开运行窗口,输入 cmd 命令打开 命令提示符窗口 或者点击开始菜单,在搜索程序和文件输入框,输入 cmd(会找到进入dos命令的cmd程序) ...

  10. 查看docker服务状态

    root用户使用 #查看docker服务状态: systemctl status docker 非root用户使用 #查看docker服务: sudo systemctl status docker