洛谷P10336 [UESTCPC 2024] 2-聚类算法

news/2024/10/4 12:16:52

涉及知识点:博弈、贪心

题意

Alice 和 Bob 在玩选点游戏,所有的点在一个 \(k\) 维空间中,他们轮流选走一个点放入自己的集合中,Alice 先手。定义集合 \(S\) 的权值 \(val(S)\) 为集合中点两两之间的 \(k\) 维曼哈顿距离之和。Alice 的得分为 \(val(S_A)-val(S_B)\),Bob 的得分为 \(val(S_B)-val(S_A)\),两人都想最大化自己的得分且采用最优策略,请问 Alice 最终得分为多少。

\(2n\) 个点,\(1\leq n\times k\leq 10^5\)

思路

将得分表示出来进行转换,则 Alice 的得分显然为:

\[\sum_{i,j\in S_A,i<j}dis(i,j)-\sum_{i,j\in S_B,i<j} \]

此时两边同时加上 \(\sum_{i,j\in S_B,i>j}dis(i,j)+\sum_{i\in S_A,j\in S_B}dis(i,j)\)

\[\begin{equation} \begin{aligned} &[\sum_{i,j\in S_A,i<j}dis(i,j)+\sum_{i,j\in S_B,i>j}dis(i,j)+\sum_{i\in S_A,j\in S_B}dis(i,j)]-[\sum_{i,j\in S_B,i<j}dis(i,j)+\sum_{i,j\in S_B,i>j}dis(i,j)+\sum_{i\in S_A,j\in S_B}dis(i,j)] \\ =& [\sum_{i,j\in S_A,i<j}dis(i,j)+\sum_{i,j\in S_B,i<j}dis(i,j)+\sum_{i\in S_A,j\in S_B}dis(i,j)]-[\sum_{i,j\in S_B}dis(i,j)+\sum_{i\in S_A,j\in S_B}dis(i,j)] \\ =& \sum_{i,j\in S_A\cup S_B,i<j}dis(i,j)-\sum_{i\in S_A\cup S_B,j\in S_B}dis(i,j) \end{aligned} \end{equation} \]

因此,Alice 的得分为空间中所有点两两之间的距离之和减去所有点到 \(S_B\) 的距离之和,前者是一个固定值,因此 Alice 想要得分更高,就必须要使得 \(S_B\) 中的点到所有点距离和最小,她得抢在 Bob 前把距离和大的点放到 \(S_A\) 中;然而由于 Bob 的得分是 Alice 的相反数,所以他想使得 \(S_B\) 中的点到所有点距离和最大,他想尽可能取距离和大的点放在 \(S_B\) 中。因此我们将每个点到所有点的距离和算出来并大到小排序,Alice 和 Bob 轮流取最大的点。

另一种很精彩的说法:

将边权化为点权,每个点的点权设为自己到其他所有点的距离之和,这样一来假设两个点 \(a,b\) 分别属于 \(S_A,S_B\) 那么它们点权相减时,它们之间的距离正好可以抵消;假设两个点属于同一个集合,那么它们之间的距离就会被算两次。因此 Alice 的得分即为 \(\frac{\sum S_A-\sum S_B}{2}\)

那么,如何计算一个点到所有点的距离和呢?肯定不能直接 \(O(n^2k)\) 计算。这里涉及一个小 Trick,由于曼哈顿距离的计算维度之间是不相干扰的,因此可以将每维拆开来计算,将所有点在该维度上的位置从小到大排序,再用前后缀和来计算,这么做是 \(O(kn\log n)\) 的,具体可以参考代码 52-61 行。

代码

用的是此处的答案统计方式。

#include<bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar __getchar
inline char __getchar(){static char ch[1<<20],*l,*r;return (l==r&&(r=(l=ch)+fread(ch,1,1<<20,stdin),l==r))?EOF:*l++;
}
#endif
template<class T>inline void rd(T &x){T res=0,f=1;char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=getchar();}while('0'<=ch && ch<='9'){res=res*10+ch-'0';ch=getchar();}x=res*f;
}
template<class T>inline void wt(T x,char endch='\0'){static char wtbuff[20];static int wtptr;if(x==0){putchar('0');}else{if(x<0){x=-x;putchar('-');}wtptr=0;while(x){wtbuff[wtptr++]=x%10+'0';x/=10;}while(wtptr--) putchar(wtbuff[wtptr]);}if(endch!='\0') putchar(endch);
}
typedef pair<int,int> pii;
typedef long long LL;
const int MAXN=2e5+5;
int n,k;
vector<int>v[MAXN];
vector<pii>dim;
LL dissum[MAXN],ans=0;
int main(){freopen("ys.in","r",stdin);freopen("ys.out","w",stdout);rd(n);rd(k);for(int i=1;i<=2*n;i++){v[i].resize(k);for(int j=0;j<k;j++){rd(v[i][j]);}}for(int i=0;i<k;i++){dim.clear();for(int j=1;j<=2*n;j++) dim.emplace_back(v[j][i],j);sort(dim.begin(),dim.end());LL presum=0;for(int j=0;j<dim.size();j++){dissum[dim[j].second]+=1LL*dim[j].first*j-presum;presum+=dim[j].first;}presum=0;for(int j=dim.size()-1;j>=0;j--){dissum[dim[j].second]+=presum-1LL*dim[j].first*(dim.size()-1-j);presum+=dim[j].first;}}sort(dissum+1,dissum+1+2*n,greater<LL>());for(int i=1;i<=2*n;i+=2)ans+=dissum[i]-dissum[i+1];wt(ans/2);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/67633.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

20241003

公交车(bus) 显然的题目,答案就是所有连通块的大小减一之和 #include <bits/stdc++.h>using namespace std;#define int long longconst int N = 1e7 + 5;int n, m, fa[N], sz[N], ans;int find(int x) {if (fa[x] == x) {return x;}return fa[x] = find(fa[x]); }void m…

C语言中对象式宏

001、不使用对象式宏[root@localhost test]# ls test.c [root@localhost test]# cat test.c ## 测试程序 #include <stdio.h>int main(void) {int i, sum = 0;int v[5] = {3, 8, 2, 4, 6}; ## 定义int【5】 型数组for(i = 0; i < 5; i…

helm学习

引用案例: 学习连接:https://www.bilibili.com/video/BV12D4y1Y7Z7/?p=7&vd_source=e03131cedc959fdee0d1ea092e73fb24 (时间:06:16)helm新建一个chart,然后删除templates里面的文件,重新编写一个,最后完成发布,更新,回滚动作1,创建一个模版的chart包,删除原来的…

折半查找法的平均查找长度(成功/失败)

转载:https://blog.csdn.net/qq_73966979/article/details/131354005

Leetcode 1498. 满足条件的子序列数目

1.题目基本信息 1.1.题目描述 给你一个整数数组 nums 和一个整数 target 。 请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。 由于答案可能很大,请将结果对 109 + 7 取余后返回。 1.2.题目地址 https://leetcode.cn/problem…

Nuxt.js 应用中的 app:beforeMount 钩子详解

title: Nuxt.js 应用中的 app:beforeMount 钩子详解 date: 2024/10/4 updated: 2024/10/4 author: cmdragon excerpt: app:beforeMount 是一个强大的钩子,允许开发者在用户界面挂载前控制应用的初始化过程。通过有效利用这一钩子,我们可以优化应用的用户体验,保持状态一致…

Log 工具打印日志

Android 采用 Log 工具打印日志, 它将各类日志划分为五个等级:Log.e: 表示错误信息, 比如可能导致程序崩溃的异常. Log.w: 表示警告信息. Log.i: 表示一般消息. Log.d: 表示调试信息, 可把程序运行时的变量值打印出来, 方便跟踪调试. Log.v: 表示冗余信息.图1图2图3

[leetcode 92] 反转链表 II

题目描述: https://leetcode.cn/problems/reverse-linked-list-ii 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。示例 1:输入:head = [1,2,3,4,5], left = 2, right = 4…