https://www.luogu.com.cn/problem/P2820
菜题一天做一道还是太浪费时间了
最小生成树,输入数据保证边权为正
但是原始图可能不连通,生成树要保证图的不连通性
问题不大,建立"超级父亲"就行了(假,见注)
不对,求最小生成树不会加原本没有的边,所以不会更改原图的连通性
所以最终选上的边数肯定会小于\(n-1\)
但是不能用\(n-[连通块个数]\)来决定选边终止的时候(假)
可以用,还是连通性与并查集的问题:一个块一旦连通,这个块就不能再加边了
不用\(n-[连通块个数]\)决定选边的代码
#include<bits/stdc++.h>
using namespace std;
#define in Read()
typedef long long ll;
int in{
int i=0,f=1; char ch=0;
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while('0'<=ch&&ch<='9') i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e4+5;
int n,m,cnt,fa[N],supfa,tot,revans;
struct edge{
int u,v,w;
edge(){};
edge(int U,int V,int W){u=U,v=V,w=W;}
friend bool operator < (const edge &a, const edge &b){return a.w<b.w;}
}e[N];
inline int get(int x){ return fa[x]==x?x:fa[x]=get(fa[x]);}
int main(){
// freopen("1.in","r",stdin);
n=in,m=in; supfa=n;
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i){
int u=in,v=in,w=in;
e[i]=edge(u,v,w);
tot+=w;
}
sort(e+1,e+m+1);
for(int i=1;i<=m;++i){
int u=get(e[i].u), v=get(e[i].v);
if(u==v) continue;
fa[u]=v;
revans+=e[i].w;
}
printf("%d\n",tot-revans);
return 0;
}
利用\(n-[连通块个数]\)优化的代码
#include<bits/stdc++.h>
using namespace std;
#define in Read()
typedef long long ll;
int in{
int i=0,f=1; char ch=0;
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while('0'<=ch&&ch<='9') i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e4+5;
int n,m,cnt,fa[N],supfa,tot,revans,block;
struct edge{
int u,v,w;
edge(){};
edge(int U,int V,int W){u=U,v=V,w=W;}
friend bool operator < (const edge &a, const edge &b){return a.w<b.w;}
}e[N];
inline int get(int x){ return fa[x]==x?x:fa[x]=get(fa[x]);}
int main(){
// freopen("1.in","r",stdin);
n=in,m=in; supfa=n;
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i){
int u=in,v=in,w=in;
e[i]=edge(u,v,w);
tot+=w;
u=get(u),v=get(v);
}
sort(e+1,e+m+1);
sort(fa+1,fa+n+1);
for(int i=1;i<=n;++i) if(fa[i]^fa[i-1]) ++block;
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i){
int u=get(e[i].u), v=get(e[i].v);
if(u==v) continue;
++cnt;
fa[u]=v;
revans+=e[i].w;
if(cnt==n-block) break;
}
printf("%d\n",tot-revans);
return 0;
}
注:上面所说的超级父亲,只是一种预处理所有连通块,然后分块求最小生成树的办法,不太好写
预处理所有连通块,然后让连通块中的节点都指向一个”超级父亲“(序号大于\(n\))
https://www.luogu.com.cn/problem/P1991
第一眼:老二分答案了
第二眼:似乎不需要二分答案,按边权从大到小选装卫星电话的哨所就行了
第三眼:还是要二分答案啊,对于每个答案,求连通块个数,连通块个数\(<S\)的合法
但这雀食不优,而且WA3个点qwq
剩下的明天de,今天肝不动了(该死的新冠qwq)
de出来了,原先用并查集计算连通块个数的方法错了
利用每次合并操作减少一个连通块得到最终的连通块个数
#include<bits/stdc++.h>
using namespace std;
#define in Read()
typedef long long ll;
int in{
int i=0,f=1; char ch=0;
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while('0'<=ch&&ch<='9') i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=600;
const double eps=1e-4;
int s,p,cnt,fa[N],block;
struct node{
double x,y;
}d[N];
struct edge{
int u,v;
double w;
edge(){};
edge(int uu,int vv,double ww){ u=uu, v=vv, w=ww;}
friend bool operator < (const edge &a, const edge &b){ return a.w<b.w;}
}e[N*N];
double l,r=2e8;
double Dist(const node &a, const node &b){ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
int get(int x){ return fa[x]==x?x:fa[x]=get(fa[x]);}
bool check(double mid){
for(int i=1;i<=p;++i) fa[i]=i; block=p;
for(int i=1;i<=cnt;++i){
if(e[i].w>mid+eps) break;
int u=get(e[i].u), v=get(e[i].v);
if(u==v) continue;
fa[u]=v; --block;
}
// printf("%d %lf\n",block,mid);
return block<=s;
}
int main(){
// freopen("1.in","r",stdin);
s=in, p=in;
for(int i=1;i<=p;++i) d[i].x=in, d[i].y=in;
for(int i=1;i<=p;++i)
for(int j=i+1;j<=p;++j)
e[++cnt]=edge(i,j,Dist(d[i],d[j]));
sort(e+1,e+cnt+1);
while(r-l>eps){
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.2lf\n",r);
return 0;
}
当然还有更好的方法:
从小到大加边,会遇到两种情况
- 再加入一条边使连通块个数从\(s+1\)变成\(s\),设此边长\(d_1\);
- 再加入一条边使连通块个数从\(s\)变成\(s-1\),设此边长\(d_2\)。
在\(d_1, d_2\)之间的边长都合法
要求最短边,那就取\(d_1\)
然后这题就随手切了
#include<bits/stdc++.h>
using namespace std;
#define in Read()
typedef long long ll;
int in{
int i=0,f=1; char ch=0;
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while('0'<=ch&&ch<='9') i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=600;
int s,p,fa[N],block,cnt;
struct node{ int x,y;}d[N];
struct edge{
int u,v;
double dis;
edge(){};
edge(int uu,int vv,double ddis){ u=uu, v=vv, dis=ddis;}
friend bool operator < (const edge &a, const edge &b){ return a.dis<b.dis;}
}e[N*N];
double dis(int u,int v){ return sqrt((d[u].x-d[v].x)*(d[u].x-d[v].x)+(d[u].y-d[v].y)*(d[u].y-d[v].y));}
int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
int main(){
// freopen("1.in","r",stdin);
s=in, p=in;
for(int i=1;i<=p;++i) fa[i]=i; block=p;
for(int i=1;i<=p;++i)
d[i].x=in, d[i].y=in;
for(int i=1;i<=p;++i)
for(int j=i+1;j<=p;++j)
e[++cnt]=edge(i,j,dis(i,j));
sort(e+1,e+cnt+1);
for(int i=1;i<=cnt;++i){
int u=get(e[i].u), v=get(e[i].v);
if(u==v) continue;
fa[u]=v;
--block;
if(block==s){ printf("%.2lf\n",e[i].dis); return 0;}
}
}
标签:连通,ch,int,28,ce,while,long,两道,getchar
From: https://www.cnblogs.com/antimony-51/p/17011037.html