返回顶部

2021牛客暑期多校训练营5 J.Jewels (二分图最小权完美匹配)

  • 题意:初始你在\((0,0,0)\),有\(n\)个宝藏,每个宝藏的坐标是\((x_i,y_i,z_i)\),每秒你都可以瞬间抓到一个宝藏,但是所有宝藏每秒都会下沉\(v_i\)个单位,那么在\(t\)秒时某个宝藏的坐标为\((x_i,y_i,z_i+t*v_i)\).抓宝藏的贡献为原点到距离的平方.

  • 题解:因为每秒都会对应一个宝藏,那么可以把问题抽象成一张二分图,右边的点对应时间,左边对应宝藏,边权就是二者相对应的贡献,那么这就是一个二分图完全最优匹配问题,我们可以使用bfs版的KM算法来解决.

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const ll INF = 1e18;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    #define int long long
    int n,m;
    int match[N];
    bool visa[N],visb[N];
    int slack[N],la[N],lb[N],wt[505][505];
    int ans;
    int x[N],y[N],z[N],v[N];
    int pre[N];
    int delta;
     
    void bfs(int x){
        int a,y=0,yy=0;
        for(int i=1;i<=n;++i)  pre[i]=0,slack[i]=INF;
        match[y]=x;
        do{
            a=match[y],delta=INF,visb[y]=true;
            for(int b=1;b<=n;++b){
                if(!visb[b]){
                    if(slack[b]>la[a]+lb[b]-wt[a][b]){
                        slack[b]=la[a]+lb[b]-wt[a][b],pre[b]=y;
                    }
                    if(slack[b]<delta) delta=slack[b],yy=b;
                }
            }
            for(int b=0;b<=n;++b){
                if(visb[b]){
                    la[match[b]]-=delta,lb[b]+=delta;
                }
                else slack[b]-=delta;
            }
            y=yy;
        }while(match[y]);
        while(y) match[y]=match[pre[y]],y=pre[y];
    }
     
    void KM(){
        for(int i=1;i<=n;++i) match[i]=la[i]=lb[i]=0;
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                visb[j]=false;
            }
            bfs(i);
        }
        for(int y=1;y<=n;++y){
            ans+=wt[match[y]][y];
        }
    }
     
    signed main() {
        scanf("%lld",&n);
        for(int i=1;i<=n;++i){
            scanf("%lld %lld %lld %lld",&x[i],&y[i],&z[i],&v[i]);
        }
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                wt[i][j]=x[i]*x[i]+y[i]*y[i]+((j-1)*v[i]+z[i])*((j-1)*v[i]+z[i]);
                wt[i][j]=-1ll*wt[i][j];
            }
        }
        KM();
        printf("%lld\n",-ans);
        return 0;
    }
    
posted @ 2021-08-12 19:18  Rayotaku  阅读(121)  评论(0编辑  收藏  举报