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; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮