写在前面
本题是一道很好的题目,本来我是用一个完全错误的做法做的,居然没有被卡,得了50pts50pts50pts。这么良心的出题人很少见了。
Solution
100pts100pts100pts做法
仔细观察本题,发现题目所求是nnn个点nnn条边的一张图,那么很容易想到是一个环套树。如何维护环套树?很显然,用并查集。
判断是否能加入一条边时,如果这条边的两端已经联通,我们需要知道这个联通块是否有环。 如果不连通,那么可以加入这条边,而且通过两端的联通块是否有环可以得到新的联通块是 否有环。
于是我们用并查集维护连通性,额外维护联通块里是否有环即可。
这样的时间复杂度可以做到O((n+m)logn)O((n+m)logn)O((n+m)logn)
Talk is Cheap, Show You the Code:
#include
using namespace std;
#define N 500005
int read(){
int sum=0,neg=1;
char c=getchar();
while(c>'9'||c<'0'&&c!='-') c=getchar();
if(c=='-') neg=-1,c=getchar();
while(c>='0'&&c<='9') sum=(sum<<1)+(sum<<3)+c-'0',c=getchar();
return sum*neg;
}
struct Edge{
int u,v,w;
Edge(int u,int v,int w):u(u),v(v),w(w){}
friend inline bool operator > (const Edge &a,const Edge &b){
return a.w>b.w;
}
};
priority_queue<Edge,vector<Edge>,greater<Edge> > q;
int n,m;
int p[N],type[N],cnt;
long long ans;
int find(int x){
return p[x]==x?x:p[x]=find(p[x]);
}
/* 50pts玄学做法(dzy大佬说可以卡掉,然而我过了50pts,QAQ)
int p[N],r[N],use[N];
bool cmp(const int i,const int j){
if(e[i].w!=e[j].w) return e[i].w
int main(){
n=read(); m=read();
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=m;i++){
int u,v,w;
u=read(); v=read(); w=read();
q.push(Edge(u,v,w));
}
while(!q.empty()){
Edge u=q.top(); q.pop();
int x=find(u.u);
int y=find(u.v);
if(x==y){
if(!type[x]) type[x]=1,ans+=u.w,++cnt;
}
else{
if(type[x]&&type[y]) continue;
p[x]=y; type[y]=type[y]|type[x];
ans+=u.w; ++cnt;
}
}
if(cnt!=n) puts("No");
else printf("%lld\n",ans);
return 0;
}