同步发布于我的网站

Problem

Sajin最近深入研究了最小生成树,现在他已经掌握了MST的算法。他渴望通过一系列查询来评估您对最小生成树概念的掌握程度。

您将面临一个加权无向图,该图包含没有任何自环的 \(n\) 个顶点和 \(m\) 条边。

Sajin提出 \(q\) 询问。对于每个顶点集,都给出了一个顶点集 \(S\) 。您的目标是确定 \(S\) 的诱导子图(induced subgraph)并找到其最小生成树的权重。如果 \(S\) 的诱导子图断开,则输出-1

图的诱导子图是另一个图,由图的顶点子集和原始图中的所有边组成,连接该子集中的顶点对。即,对于图 \(G=(V,E)\) ,给定 \(V^\prime\) ,则有 \(E^\prime=\{(u,v) \mid u,v\in V^\prime,(u,v)\in E\}\),诱导子图为 \(G^\prime=(V^\prime,E^\prime)\)。

\(2\le n\le 10^5,1\le m,q\le10^5\)

\(1 \le |S_i|\le n,\sum S_i\le 10^5\)

Solution

Algorithm1

由于限制了 \(\sum S_i\le 10^5\) , MST本身的时间复杂度是完全过得去的。但是瓶颈在于如何从 \(G\) 中挑出需要的边。

考虑两种找边的方法:

  1. 对于 \(S\) 中的点 \(x\),遍历 \(x\) 的所有出边,终点为 \(y\) 。如果 \(y\in S\) ,那么边 \((x,y)\in G^\prime\) 。将得到的所有边存起来并排序跑kruskal。

​ 对于一次询问,这个算法的最坏时间复杂度为 \(O(m\log(|S|))\) ,即所有边都需要被遍历一次。

  1. 用map存图 \(G\) ;双重遍历 \(x,y\in S\) ,将其中存在的边 \((x,y)\) 取出来并排序跑kruskal。

​ 整体时间复杂度是\(O(n^2+n^2\log(n))\),即双重遍历和kruskal。

取 \(base=\sqrt n\) ,

  • 当 \(|S|\le base\) 时使用第二种算法,总时间复杂度为 \(O(n\cdot base\cdot \log(n))\)
  • 当 \(|S| > base\) 时使用第一种算法,总时间复杂度为 \(O(base\cdot m\cdot \log(n))\) 。

Algorithm2

一般建立最小生成树,我们会使用上面的第一种找边的方式:对于 \(S\) 中的点 \(x\),遍历 \(x\) 的所有出边,终点为 \(y\) 。如果 \(y\in S\) ,那么边 \((x,y)\in G^\prime\) 。将得到的所有边存起来并排序跑kruskal。

问题的瓶颈在于新建导出子图 \(G^\prime\) 的时候,会被类菊花图卡掉。可能会一直询问类菊花图的核心点,而这些核心点具有很多条出边,每次询问都需要遍历核心节点的出边。

考虑类似三元环的连边方式,对于每条边,只从度小的点到度大的点连一条单向边。如果度数相同,则从编号小的连接到编号大的。

在这样构建的图中,每个点的出度不会大于 \(\sqrt m\) 。如果有某个点的出度为 \(d > \sqrt m\) ,那么在原图中,需要有 \(d\) 个度数大于 \(d\) 的节点与该节点连接,此时总边数至少 \(d\times d>m\)​,矛盾。

所以可以在这样的一张单向图中放心的找边,单次询问时间复杂度 \(O(|S| \sqrt m \log(|S|))\)​ 。

Code

Code1

#define N 100010
struct Edge
{
int x,y;
LL w;
Edge(int xx,int yy,LL ww)
{
x=xx;
y=yy;
w=ww;
}
bool operator<(const Edge & z)
{
return w<z.w;
}
}; int n,m,q; namespace algo1{
int cnt;
int head[N],nxt[N*2],ver[N*2],w[N*2];
void insert(int x,int y,LL z)
{
nxt[++cnt]=head[x];
head[x]=cnt;
ver[cnt]=y;
w[cnt]=z;
} }; namespace algo2{
map<pii,LL>mp;
void insert(int x,int y,LL z)
{
pii e=make_pair(x,y);
if(mp.find(e)==mp.end()) mp[e]=z;
else mp[e]=min(mp[e],z);
}
bool exist(pii e)
{
return mp.find(e)!=mp.end();
}
}; namespace DSU{
int vis[N],f[N],sz[N]; int getf(int x)
{
if(x==f[x]) return x;
return f[x]=getf(f[x]);
} void merge(int x,int y)
{
x=getf(x);
y=getf(y);
if(x==y) return;
if(sz[x]<sz[y]) swap(x,y);
f[y]=x;
sz[x]+=sz[y];
} bool ask(int x,int y)
{
return getf(x)==getf(y);
}
}; LL MST(vector<int>V,vector<Edge>E)
{
sort(E.begin(),E.end());
LL ans=0;
int cnt=1;
for(unsigned int i=0;i<E.size();i++)
{
if(!DSU::ask(E[i].x,E[i].y))
{
cnt++;
DSU::merge(E[i].x,E[i].y);
ans+=E[i].w;
}
}
if(cnt!=V.size()) return -1;
return ans;
} int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t=1;
// cin>>t;
while(t--)
{
cin>>n>>m>>q;
for(int i=1;i<=m;i++)
{
int x,y;
LL z;
cin>>x>>y>>z;
algo1::insert(x,y,z);
algo1::insert(y,x,z);
algo2::insert(x,y,z);
algo2::insert(y,x,z);
}
const int base=sqrt(100000);
while(q--)
{
int k;
cin>>k;
vector<int>S;
vector<Edge>E;
for(int i=0;i<k;i++)
{
int t;cin>>t;
S.push_back(t);
DSU::vis[t]=1;
DSU::sz[t]=1;
DSU::f[t]=t;
}
if(k>base)
{
//algo1
for(int i=0;i<k;i++)
{
int x=S[i];
for(int j=algo1::head[x];j;j=algo1::nxt[j])
{
int y=algo1::ver[j];
LL w=algo1::w[j];
if(DSU::vis[y])
{
E.push_back(Edge(x,y,w));
}
}
}
}
else
{
//algo2
for(int i=0;i<k;i++)
{
for(int j=i+1;j<k;j++)
{
pii e=make_pair(S[i],S[j]);
if(algo2::exist(e))
{
E.push_back(Edge(S[i],S[j],algo2::mp[e]));
}
}
}
} cout<<MST(S,E)<<endl; for(int i=0;i<k;i++)
{
DSU::vis[S[i]]=0;
}
}
}
return 0;
}

生最差劲的一件事莫过于早上写的代码但半路跑路还没做任何标记,晚上打开不知道从何续写。

人生最美好的一件事莫过于发现这份代码其实是以及写完的!?而且还能过样例!??而且直接提交直接过了!???

Code2

#define N 100010
struct Edge
{
int x,y;
LL w;
Edge(int xx,int yy,LL ww)
{
x=xx;
y=yy;
w=ww;
}
bool operator<(const Edge & z)
{
return w<z.w;
}
}; int n,m,q; namespace G1
{
map<pii,LL>mp;
int degree[N];
void insert(int x,int y,LL z)
{
pii e=make_pair(x,y);
if(mp.find(e)==mp.end()) mp[e]=z;
else mp[e]=min(mp[e],z);
degree[x]++;
}
}; namespace G2
{
int cnt;
int head[N],nxt[N*2],ver[N*2];
LL w[N];
void insert(int x,int y,LL z)
{
nxt[++cnt]=head[x];
head[x]=cnt;
ver[cnt]=y;
w[cnt]=z;
}
}; namespace DSU{
int vis[N],f[N],sz[N]; int getf(int x)
{
if(x==f[x]) return x;
return f[x]=getf(f[x]);
} void merge(int x,int y)
{
x=getf(x);
y=getf(y);
if(x==y) return;
if(sz[x]<sz[y]) swap(x,y);
f[y]=x;
sz[x]+=sz[y];
} bool ask(int x,int y)
{
return getf(x)==getf(y);
}
}; LL MST(vector<int>V,vector<Edge>E)
{
sort(E.begin(),E.end());
LL ans=0;
int cnt=1;
for(unsigned int i=0;i<E.size();i++)
{
if(!DSU::ask(E[i].x,E[i].y))
{
cnt++;
DSU::merge(E[i].x,E[i].y);
ans+=E[i].w;
}
}
if(cnt!=V.size()) return -1;
return ans;
} int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t=1;
// cin>>t;
while(t--)
{
cin>>n>>m>>q;
for(int i=1;i<=m;i++)
{
int x,y;
LL z;
cin>>x>>y>>z;
G1::insert(x,y,z);
G1::insert(y,x,z);
} for(auto it=G1::mp.begin();it!=G1::mp.end();it++)
{
int x=it->first.first,y=it->first.second;
LL w=it->second;
if(G1::degree[x]<G1::degree[y]||(G1::degree[x]==G1::degree[y]&&x<y)) G2::insert(x,y,w);
} while(q--)
{
int k;
cin>>k;
vector<int>S;
vector<Edge>E;
for(int i=0;i<k;i++)
{
int t;cin>>t;
S.push_back(t);
DSU::vis[t]=1;
DSU::sz[t]=1;
DSU::f[t]=t;
}
for(int i=0;i<k;i++)
{
int x=S[i];
for(int j=G2::head[x];j;j=G2::nxt[j])
{
int y=G2::ver[j];
LL w=G2::w[j];
if(DSU::vis[y])
{
E.push_back(Edge(x,y,w));
}
}
} cout<<MST(S,E)<<endl; for(int i=0;i<k;i++)
{
DSU::vis[S[i]]=0;
}
} }
return 0;
}

2024牛客多校2B MST的更多相关文章

  1. 2019牛客多校第一场 I Points Division(动态规划+线段树)

    2019牛客多校第一场 I Points Division(动态规划+线段树) 传送门:https://ac.nowcoder.com/acm/contest/881/I 题意: 给你n个点,每个点有 ...

  2. 牛客多校第一场 B Inergratiion

    牛客多校第一场 B Inergratiion 传送门:https://ac.nowcoder.com/acm/contest/881/B 题意: 给你一个 [求值为多少 题解: 根据线代的知识 我们可 ...

  3. 2019牛客多校第二场 A Eddy Walker(概率推公式)

    2019牛客多校第二场 A Eddy Walker(概率推公式) 传送门:https://ac.nowcoder.com/acm/contest/882/A 题意: 给你一个长度为n的环,标号从0~n ...

  4. 牛客多校第三场 F Planting Trees

    牛客多校第三场 F Planting Trees 题意: 求矩阵内最大值减最小值大于k的最大子矩阵的面积 题解: 矩阵压缩的技巧 因为对于我们有用的信息只有这个矩阵内的最大值和最小值 所以我们可以将一 ...

  5. 牛客多校第三场 G Removing Stones(分治+线段树)

    牛客多校第三场 G Removing Stones(分治+线段树) 题意: 给你n个数,问你有多少个长度不小于2的连续子序列,使得其中最大元素不大于所有元素和的一半 题解: 分治+线段树 线段树维护最 ...

  6. 牛客多校第四场sequence C (线段树+单调栈)

    牛客多校第四场sequence C (线段树+单调栈) 传送门:https://ac.nowcoder.com/acm/contest/884/C 题意: 求一个$\max {1 \leq l \le ...

  7. 牛客多校第3场 J 思维+树状数组+二分

    牛客多校第3场 J 思维+树状数组+二分 传送门:https://ac.nowcoder.com/acm/contest/883/J 题意: 给你q个询问,和一个队列容量f 询问有两种操作: 0.访问 ...

  8. 2019牛客多校第八场 F题 Flowers 计算几何+线段树

    2019牛客多校第八场 F题 Flowers 先枚举出三角形内部的点D. 下面所说的旋转没有指明逆时针还是顺时针则是指逆时针旋转. 固定内部点的答案的获取 anti(A)anti(A)anti(A)或 ...

  9. 2019年牛客多校第一场B题Integration 数学

    2019年牛客多校第一场B题 Integration 题意 给出一个公式,求值 思路 明显的化简公式题,公式是分母连乘形式,这个时候要想到拆分,那如何拆分母呢,自然是裂项,此时有很多项裂项,我们不妨从 ...

  10. 2020牛客多校第八场K题

    __int128(例题:2020牛客多校第八场K题) 题意: 有n道菜,第i道菜的利润为\(a_i\),且有\(b_i\)盘.你要按照下列要求给顾客上菜. 1.每位顾客至少有一道菜 2.给顾客上菜时, ...

随机推荐

  1. implicit和explicit求解器的一点比较

    implicit procedure和explicit procedure的比较 abaqus有两个求解器:standard和 explicit求解器.两个求解器在很多方面都有所差异:单元类型/材料行 ...

  2. 【MATLAB习题】双摇杆机构的运动学分析

    1.双摇杆机构概述 双摇杆机构的判别方法: 最长杆长度+最短杆长度 ≤ 其他两杆长度之和,连杆(机架的对杆)为最短杆时. 如果最长杆长度+最短杆长度 >其他两杆长度之和,此时不论以何杆为机架,均 ...

  3. 【MATLAB习题】牛头刨床机构的运动学分析

    1. 数学模型 已知牛头刨床主运动机构各构件的尺寸为: \(l1=125mm,l3=600mm,l4=150mm,l6=275mm,l'6=575mm\),原动件1以匀角速度ω1=1rad/s逆时针转 ...

  4. 关于centos 7安装binwalk的过程中产生的问题

    啊,kali机坏了,又安的centos o(╥﹏╥)o 但是centos没有binwalk,它也不能像kali机一样之间install 又在网上搜教程 https://blog.csdn.net/qq ...

  5. Netty基础—6.Netty实现RPC服务

    大纲 1.RPC的相关概念 2.RPC服务调用端动态代理实现 3.Netty客户端之RPC远程调用过程分析 4.RPC网络通信中的编码解码器 5.Netty服务端之RPC服务提供端的处理 6.RPC服 ...

  6. go cobra实例讲解

    概述 cobra 库是 golang 的一个开源第三方库,能够快速便捷的建立命令行应用程序. 优势:cobra 可以快速建立CLI程序,使我们更专注于命令需要处理的具体的业务逻辑. 举两个例子: hu ...

  7. linux防火墙查看状态firewall

    一.firewall防火墙 1.查看firewall服务状态 systemctl status firewalld 出现Active: active (running)切高亮显示则表示是启动状态. 出 ...

  8. 从零开始:基于 PyTorch 的图像分类模型

    摘要:本文详细记录了使用 PyTorch 从零搭建一个图像分类模型的过程,涵盖卷积神经网络(CNN).数据预处理.模型设计.训练调试与优化.通过对 CIFAR-10 数据集的处理实践,结合经典文献和 ...

  9. IE 条件注释

    参考文档 IE6 IE7 IE8 IE9 IE10 Css hack及IE条件注释法 IE的有条件注释判定IE版本详解(附实例代码)

  10. DeepSeek 会话补全 API

    DeepSeek 会话补全 API 是一个超强大的 AI 对话接口 ,可以让你: 打造自己的 智能聊天机器人 让 AI 帮你 写文章.改代码.编故事 甚至模拟 各种角色(比如猫娘.霸道总裁.科幻作家- ...