P9666 题解
P9666 [ICPC2021 Macao R] Link-Cut Tree 题解
题目大意
\(t\) 组数据,每组数据给出一张点的数量为 \(n\),边的数量为 \(m\) 的无向图。按照输入顺序,第 \(i\) 条边的边权为 \(2^i\),要求找到一个边数 \(s>2\) 的简单环,且得边权最小,按照升序输出构成这个简单环的边的序号,若无解输出 \(−1\)。
最小生成树(Kruskal)+贪心
当任意两个点在同一联通块时,连接这两个点后必定为环,那么问题就转化为了最小生成树问题。
由于 \(2^{i-1}<2^i\) 必定成立,那么贪心策略必定成立,即第一个出现的环边权必定最小。
由于输入保证单调,即 \(2^{i-1}<2^i\),那么 Kruskal 中的 sort 可省略。
时间复杂度

CODE
#include<bits/stdc++.h>
#define int long long
const int N=1e5+5;
struct node{
int v;
int id;
node(int vv,int iid):v(vv),id(iid){}
};
int n,m,st,ed,se;
int fa[N];
std::vector<std::pair<int,int> > b;
std::vector<node> to[N];
std::vector<int> s;
inline int find(int x){//并查集
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
inline void init(){//初始化
b.clear();
s.clear();
for(int i=1;i<=n;i++){
to[i].clear();
fa[i]=i;
}
return;
}
inline void read(){//读入
std::cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
std::cin>>u>>v;
b.push_back(std::make_pair(u,v));
}
return;
}
inline bool kruskal(){//最小生成树
int i=0;
for(std::vector<std::pair<int,int> >::iterator it=b.begin();it!=b.end();it++){
i++;
int u=(*it).first,v=(*it).second;
to[u].push_back(node(v,i));
to[v].push_back(node(u,i));
int x=find(u),y=find(v);
if(x!=y){
fa[x]=y;
continue;
}
st=u;
ed=v;
se=i;
return 1;
}
return 0;
}
inline bool dfs(int v,int u,int f){//找路线
if(u==v) return 1;
for(std::vector<node>::iterator it=to[u].begin();it!=to[u].end();it++){
if((*it).v==f) continue;
s.push_back((*it).id);
if(dfs(v,(*it).v,u)) return 1;
s.pop_back();
}
return 0;
}
inline void print(bool f){//输出
if(f){
s.push_back(se);
dfs(ed,st,st);
sort(s.begin(),s.end());
for(std::vector<int>::iterator it=s.begin();it!=s.end();it++)
std::cout<<*it<<' ';
std::cout<<'\n';
return;
}
std::cout<<-1<<'\n';
return;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
int t;
std::cin>>t;
for(int i=1;i<N;i++)
fa[i]=i;
while(t--){
init();
read();
print(kruskal());
}
return 0;
}

浙公网安备 33010602011771号