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

思路

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

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

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

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

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

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

然后我们就可以使用广义串并联图的经典操作:删 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. 中国最难入职的IT公司排行榜

    在IT行业竞争日益白热化的今天,头部企业的招聘门槛不断刷新求职者的认知.根据最新行业调研和招聘数据,我们整理出2025年中国最难入职的几家互联网公司,并揭秘其背后严苛的选拔逻辑. 通常衡量难不难,会从 ...

  2. element-ui中el-table多层数组渲染问题

    tableData: [ { name: '国家出资人', list: [ { name: '2011', value: '0' }, { name: '2012', value: '0' }, { ...

  3. Kafka - server.properties参数详解

    server.properties #broker的全局唯一编号,不能重复 broker.id=0 #用来监听链接的端口(kafka端口号),producer或consumer将在此端口建立连接 po ...

  4. C++基础练习案例 - 模拟时钟系统 [DOS]

    大学时期初学C++,做了些案例练习,想着整理一下,供自己和网友翻阅参考,谢谢支持!有个[模拟时钟系统]做的还可以. [PS]存在一些小问题,如编写习惯等,文末有简单小结,请自行辨析.算是提供不良模板, ...

  5. CPrimerPlus

    还没学 的 167页的wordcnt程序 199页的checking程序(太长了,不想看) 113页的第八章编程练习5(不想看) 125页的复习题9(有问题,有时间再来验证) 119页重定向和文件(n ...

  6. 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库

    前言 今天大姚给大家分享一套基于 Material Design 规范实现的.开源(MIT license)且免费的 Blazor 和 Razor 通用组件库:MatBlazor. Blazor介绍 ...

  7. php stripslashes 函数的意思

    脑子不行了,很多东西看过就忘,比如这个stripslashes,知道是去除反斜杠,但为啥用它死活想不起来,搜索一下,把这几篇文章抄下来: 1.反斜杠是怎么回事 两个东西 ini_set(magic_q ...

  8. C# fleck websocket使用

    转载于:https://www.itspeeding.com/article/28 1.web页面 1 <html lang="en" xmlns="http:// ...

  9. 本地部署overleaf服务帮助latex论文编写

    是的,overleaf是一个很好的服务,提供了立刻上手就可以编写的latex文章的服务.但是,overleaf会面对latex超时,所以需要付钱的情况,这常出现在编写期刊的论文的情况. 因为时效性,所 ...

  10. cpp简单总结

    1.简述智能指针的特点,简述new和malloc的区别. shared_ptr,显现共享式特点,多个同类型的shared指针可以共享一个对象,当持有者的计数归0,shared_ptr指向的指针就会被释 ...