10.5最小生成树考试总结

· · 个人记录

题目总结

T1

正确思路:

一个很模版的模版题,只要算出两点之间的距离,然后跑最小生成树,注意边权小于c的边不可以通过。

AC Code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=5e6+10;
struct node{
    int x,y,w;
}e[N];
bool cmp(node x,node y){
    return x.w<y.w;
}
int n,c,x[N],y[N],tot,fa[N],cnt,ans;
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void unionn(int x,int y){
    x=find(x); y=find(y);
    if(x!=y){
        fa[x]=y;
    }
    return ;
}
int dist(int i,int j){
    int a=(x[i]-x[j])*(x[i]-x[j]);
    int b=(y[i]-y[j])*(y[i]-y[j]);
    int q=a+b;
    return q;
}
void kruskal(){
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=tot;i++){
        if(find(e[i].x)==find(e[i].y)) continue;
        if(e[i].w<c) continue;//边权小于c不能走
        ans+=e[i].w;
        unionn(e[i].x,e[i].y);
        cnt++;
        if(cnt==n-1) return ; 
    }
    return ;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>c;
    for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
    tot=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            e[++tot].x=i;
            e[tot].y=j;
            e[tot].w=dist(i,j);//算出任意两点距离
        }
    }
    sort(e+1,e+1+tot,cmp);
    kruskal();//跑kruskal算法,prim更好但我没学
    if(cnt<n-1) cout<<-1;
    else cout<<ans;
    return 0;
} 

T2

错误原因

开了double,原题的距离不需要开根号,所以不用也不能开double。

正确思路

一道更模版的板子题,只需要求两点距离,然后跑最小生成树,并记录最后连接的边权,因为排了序,所以最后的就是最大的边。

AC Code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e6+10;
struct node{
    int x,y,w;
}e[N];
bool cmp(node x,node y){
    return x.w<y.w;
}
int n,tot,fa[N],cnt,ans,x[N],y[N];
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void unionn(int x,int y){
    x=find(x); y=find(y);
    if(x!=y){
        fa[x]=y;
    }
    return ;
}
double dist(int i,int j){
    double a=(x[i]-x[j])*(x[i]-x[j]);
    double b=(y[i]-y[j])*(y[i]-y[j]);
    double q=a+b;
    return q;
}
void kruskal(){
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=tot;i++){
        if(find(e[i].x)==find(e[i].y)) continue;
        ans=max(ans,e[i].w);
        unionn(e[i].x,e[i].y);
        cnt++;
        if(cnt==n-1) return ; 
    }
    return ;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
    tot=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            e[++tot].x=i;
            e[tot].y=j;
            e[tot].w=dist(i,j);
//          cout<<e[tot].w<<endl;
        }
    }
    sort(e+1,e+1+tot,cmp);
    kruskal();
    cout<<ans;
    return 0;
}

T3

错误原因

没开longlong

正确思路

还是比较模版,跟上一题一样,只要多记录几个值:用的是点权还是边权,并输出,题目有点长,但是还算简单\

一定要开longlong!!别问我咋知道的

AC Code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long 
typedef long long ll;
const int N=4e6+10;
struct node{
    int x,y,w;
}e[N];
bool cmp(node x,node y){
    return x.w<y.w;
}
int n,tot,fa[N],ans,sum1,sum2,x[N],y[N],c[N],k[N];
vector<int> res1;
vector<node> res2;
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void unionn(int x,int y){
    x=find(x); y=find(y);
    if(x!=y){
        fa[x]=y;
    }
    return ;
}
int dist(int i,int j){
    int a=abs(x[i]-x[j]);
    int b=abs(y[i]-y[j]);
    return a+b;
}
int kruskal(){
    for(int i=1;i<=n+1;i++) fa[i]=i;
    for(int i=1;i<=tot;i++){
        if(find(e[i].x)==find(e[i].y)) continue;
        ans+=e[i].w;
        if(e[i].x==n+1) res1.push_back(e[i].y);
        else res2.push_back({e[i].x,e[i].y});
        unionn(e[i].x,e[i].y);
    }
    return ans;
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
    for(int i=1;i<=n;i++) cin>>c[i];
    for(int i=1;i<=n;i++) cin>>k[i];
    tot=0;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            e[++tot].x=i;
            e[tot].y=j;
            e[tot].w=dist(i,j)*(k[i]+k[j]);
        }
    }
    for(int i=1;i<=n;i++){
        e[++tot].x=n+1;
        e[tot].y=i;
        e[tot].w=c[i];
    }
    sort(e+1,e+1+tot,cmp);
    cout<<kruskal()<<endl;
    cout<<res1.size()<<endl;
    for(auto x : res1) cout<<x<<' ';
    cout<<endl;
    cout<<res2.size()<<endl;
    for(auto x : res2) cout<<x.x<<' '<<x.y<<endl;
    return 0;
}

T4

正确思路

这题不能暴力,时间空间都炸了,所以我们以区间为单位处理,我们发现每次加边都是一个连通块,而连通块必然是一片连续的区间,假设我们在维护并查集的时候,尽量往右边合并,那么在数区间时,可以直接跳到这个连通块的右端点,跳过了中间很多循环遍历的过程,一直到这个点该联的区间即可。

AC Code

#include<bits/stdc++.h>
#define int long long
#define qwq i
using namespace std;
const int N = 2e5+5;
struct node{
    int l,r,c;
    bool operator < (const node & tmp) const {
        return c < tmp.c;
    }
}a[N];
int n,q,fa[N];
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
int kruskal() {
    for (int i=1; i<=n; i++) fa[i] = i;
    int sum=0;
    for (int i=1;i<=q;i++) {
        int l=a[i].l,r=a[i].r,c=a[i].c;
        sum+=c;  
        l=find(l),r=find(r);
        while(l!=r){
            sum+=c;
            fa[l]=l+1;
            l=find(l);
        }
    }
    if(find(1)==find(n)) return sum;
    return -1;
}
signed main() {
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n>>q;
    for(int i=1;i<=q;i++) cin>>a[i].l>>a[i].r>>a[i].c;
    sort(a+1,a+1+q);
    cout<<kruskal();
    return 0;
}

总结

这次有很多基础题都丢了分,还需巩固。