贪心、博弈论

将点 $i$ 和点 $j$ 在所有维度的距离和 $\sum dis_{i,j}$ 视为边 $(i,j)$ 的边权,把边权同时加到两个节点的贡献里。

如果节点 $i,j$ 被同一个人选择,这个人会获得双倍边权;如果没有,两个人的收益之差不会受到影响,所以对答案也没有影响。

容易确定贪心策略为优先选择贡献大的节点,算出每个节点的贡献之后就可以开始选择。计算节点贡献时 $k$ 个维度是独立的,排序后拆绝对值可以完成对一个维度的贡献计算。

注意答案要在完成累加之后除以二,直接把每个点的贡献除以二会导致点贡献下取整,计算出错。

时间复杂度 $O(nk \log n)$。

#include<bits/stdc++.h>
using namespace std;
int n,k,*a[200'005],p[200'005];
long long ans,sum[200'005];
void calculate(int x){
    long long pre=0ll,suf=0ll;
    for(int i=1;i<=n;i++) suf+=a[i][x];
    for(int i=1;i<=n;i++){
        sum[p[i]]+=1ll*(i-1)*a[p[i]][x]-pre;
        pre+=a[p[i]][x],suf-=a[p[i]][x];
        sum[p[i]]+=suf-1ll*(n-i)*a[p[i]][x];
    }
}
int main(){
    scanf("%d%d",&n,&k);
    n<<=1,iota(p+1,p+n+1,1);
    for(int i=1;i<=n;i++){
        a[i]=new int[k+5];
        for(int j=1;j<=k;j++) scanf("%d",&a[i][j]);
    }
    for(int i=1;i<=k;i++){
        sort(p+1,p+n+1,[&](auto x,auto y){return a[x][i]<a[y][i];});
        calculate(i);
    }
    sort(sum+1,sum+n+1,greater<long long>());
    for(int i=1;i<=n;i++){
        if(i&1) ans+=sum[i];
        else ans-=sum[i];
    }
    ans>>=1;
    printf("%lld\n",ans);
    return 0;
}
最后修改:2024 年 10 月 22 日
如果觉得我的文章对你有用,请随意赞赏