NC218425 魏迟燕的自走棋(贪心+并查集)

第一步贪心,按照权值排序,越大的能用肯定最优

因为在当前情况下,假如可以用这个但不用

最后求取的解,你将他对应的那个换掉,一个不会更差。

排完序后我们再考虑本题。

这题巧妙地转化在于,转化成图模型,每个武器的两个端点相当于一条边,如果k=1就是自环

对于一个大小为x的集合,他们中的边要不是x-1,要不是x(如果没有一个武器能给某个点,这个点没有意义不讨论)

如果边等于x-1,相当于这个有x个人的集合里面有x-1个武器,因此一个人是空闲的,且我们可以通过边传递武器,使得任何一个空闲都可以

那么边等于x代表没人空闲。

我们按大到小枚举边的时候,就相当于考虑并查集的思路,如果他们本来已经在一个集合里面了,就看他们是不是还可以加一条边

如果他们不再一个集合,那么如果两个集合有一个还可以加就可以加

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int p[N],st[N];
int n,m;
struct node{
    int a,b,x;
}s[N];
int find(int x){
    if(p[x]!=x){
        p[x]=find(p[x]);
    }
    return p[x];
}
bool cmp(node a,node b){
    return a.x>b.x;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    int i;
    for(i=1;i<=n;i++){
        p[i]=i;
        st[i]=0;
    }
    for(i=1;i<=m;i++){
        int k;
        cin>>k;
        int a,b,c;
        if(k==1){
            cin>>a>>c;
            s[i]={a,a,c};
        }
        else{
            cin>>a>>b>>c;
            s[i]={a,b,c};
        }
    }
    sort(s+1,s+1+m,cmp);
    ll sum=0;
    for(i=1;i<=m;i++){
        int a=s[i].a,b=s[i].b,c=s[i].x;
        int pa=find(a),pb=find(b);
        if(pa!=pb&&(!st[pa]||!st[pb])){
            p[pb]=pa;
            sum+=c;
            if(st[pa]||st[pb]){
                st[pa]=1;
            }
        }
        else if(!st[pa]){
            st[pa]=1;
            sum+=c;
        }
    }
    cout<<sum<<endl;
    return 0;
}
View Code

 

posted @ 2021-02-25 16:43  朝暮不思  阅读(74)  评论(0编辑  收藏  举报