同步发布于我的网站

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. TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合

    \3c span id="mce_marker" data-mce-type="bookmark">\3c /span>\3c span id=&q ...

  2. delphi 弹出输入框的InputQuery, InputQuery 函数用法

    delphi 弹出输入框的InputQuery, InputQuery 函数用法 procedure TForm1.Button1Click(Sender: TObject); var str: st ...

  3. Docker 初始镜像 scratch

    初识 scratch 有那么一天,我们在这里邂逅了镜像scratch... 先来搜索下这个镜像 docker search scratch NAME DESCRIPTION STARS OFFICIA ...

  4. 学习unigui【18】unidbgrid的GridsGroupingSorting

    折腾二天,你不按照demo里的代码来,就是没有效果.功力不够导致的.学习学习再学习!努力努力再努力! procedure TUniGridsGroupingSorting.UniDBGrid1Mult ...

  5. study Rust-6【使用结构体组织相关联的数据】

    struc(structure) 定义并且实例化结构体: struct User { username: String, email: String, sign_in_count: u64, acti ...

  6. CH39x产品介绍

    CH39x产品介绍 芯片 CH392 CH395 CH390 接口 异步串口/SPI 异步串口/SPI/8位并口(仅CH395L支持) SPI/16位或8位并口(仅CH390L支持) 网口速率 10M ...

  7. 如何使用Nacos作为配置中心统一管理配置

    如何使用Nacos作为配置中心统一管理配置 1).引入依赖, <dependency> <groupId>com.alibaba.cloud</groupId> & ...

  8. python Response的语法

    -- r.status_code #响应状态码 -- r.content #字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩 -- r.headers #以字典对象存储服务器响应头 ...

  9. Stream流式编程工具类,开发必备

    把自己写的流式编程工具分享出来,不涉及公司业务,非常便捷,不用在业务层看到一条龙式的Stream代码了: 大家用的最多的应该是转list,转set,以及setVFromE: 觉得好用点个赞就行 imp ...

  10. access vba实现OLE对象保存到本地

    参考oletodisk的实现方法,更新为在64位office上野可以运行,函数模块代码如下: 1 Option Compare Database 2 Option Explicit 3 4 5 'DE ...