2020 ICPCアジア台北-新竹サイトプログラミングコンテストH.UltraNetの最適化(2ポイント+最小スパニングツリー+計算の貢献)

2020ICPCアジア台北-新竹サイトプログラミングコンテストH.UltraNetの最適化(2ポイント+最小スパニングツリー+計算の貢献)

元のタイトルリンク

題名:

最小エッジ重みが最大になるようにスパニングツリーを見つけます。これに基づいて、任意の2点パスの最短エッジ重みの合計が最小になります。任意の2点のパス上の最短の辺の重みの合計を見つけます。

アイデア:

最小の辺が最大の重みを持ち、二分法を考えるのは簡単です。

二分最小エッジ重みの位置は、スパニングツリーを構成できるかどうかを毎回チェックされます。

答えの計算では、各エッジの寄与を考慮してください。選択したエッジを小さいものから大きいものに並べ替えます。現在のエッジが2つの接続されたブロックを接続できる場合、現在のエッジの寄与はw * siz [fu] * siz [fv]です。

コード:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;typedef unsigned long long ull;
typedef pair<ll,ll>PLL;typedef pair<int,int>PII;typedef pair<double,double>PDD;
#define I_int ll
inline ll read(){
    
    ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}return x*f;}
#define read read()
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
ll ksm(ll a,ll b,ll p){
    
    ll res=1;while(b){
    
    if(b&1)res=res*a%p;a=a*a%p;b>>=1;}return res;}
const int maxn=1e6+7;

struct node{
    
    
    ll u,v,w;
}edge[maxn];
bool cmp(node a,node b){
    
    
    return a.w<b.w;
}
bool cmp1(node a,node b){
    
    
    return a.w>b.w;
}
int n,m,root[maxn];

int Find(int x){
    
    
    if(x!=root[x]) root[x]=Find(root[x]);
    return root[x];
}

int h[maxn],idx;
struct node1{
    
    
    ll e,ne,w;
}edge1[maxn];
void add1(int u,int v,int w){
    
    
    edge1[idx]={
    
    v,h[u],w};h[u]=idx++;
}

bool check(int mid){
    
    
    if(m-mid+1<n-1) return 0;
    int cnt=0;
    rep(i,1,n) root[i]=i;
    rep(i,mid,m){
    
    
        int u=edge[i].u,v=edge[i].v,w=edge[i].w;
        int fu=Find(u),fv=Find(v);
        if(fu!=fv){
    
    
            root[fu]=fv;
            cnt++;
            //add1(u,v,w);add1(v,u,w);
        }
        if(cnt==n-1) break;
    }
    if(cnt==n-1) return 1;
    return 0;
}
ll siz[maxn];
void solve(){
    
    
    n=read,m=read;
    //rep(i,1,n) root[i]=i;
    rep(i,1,m){
    
    
        ll u=read,v=read,w=read;
        edge[i]={
    
    u,v,w};
    }
    sort(edge+1,edge+1+m,cmp);
    int l=1,r=m,ans;//枚举最短边所在的位置
    //最短边最长
    while(l<=r){
    
    
        int mid=(l+r)/2;
        if(check(mid)){
    
    //还可以更长
            ans=mid;l=mid+1;
        }
        else r=mid-1;
    }

    memset(h,-1,sizeof h);
    int cnt=0;
    rep(i,1,n) root[i]=i;
    rep(i,ans,m){
    
    
        ll u=edge[i].u,v=edge[i].v,w=edge[i].w;
        int fu=Find(u),fv=Find(v);
        if(fu!=fv){
    
    
            root[fu]=fv;
            cnt++;
           // add1(u,v,w);add1(v,u,w);
           edge[cnt]={
    
    u,v,w};
        }
        if(cnt==n-1) break;
    }
    sort(edge+1,edge+1+cnt,cmp1);
    ll res=0;
    rep(i,1,n) root[i]=i,siz[i]=1;
    rep(i,1,cnt){
    
    
        ll u=edge[i].u,v=edge[i].v,w=edge[i].w;
        int fu=Find(u),fv=Find(v);
        if(fu!=fv){
    
    
            root[fu]=fv;
            res=res+w*siz[fu]*siz[fv];
            siz[fv]+=siz[fu];
        }
    }
    printf("%lld\n",res);
}
int main(){
    
    
    int T=1;
    while(T--) solve();
    return 0;
}

おすすめ

転載: blog.csdn.net/weixin_45675097/article/details/113862012