题意:
给一个n*m的矩阵,每个位置有个颜色值,相同颜色值两两求曼哈顿距离,问总和是多少(选取顺序不同也算不同,即两个点会产生两次贡献)。
思路:
假设 n = m = 1 0 3 n=m=10^3 n=m=103,那么时间复杂度最坏要维持在 O ( n 2 l o g n ) O(n^2log n) O(n2logn)。如果对每个点进行与其他相同颜色的点的求和,这个求和就需要是在 O ( l o g n ) O(logn) O(logn)以内的。考虑一种颜色,因为是曼哈顿距离所以x和y可以分开来求,整体求是不太可能的,因为每个点都需要和其他所有点都算一遍,考虑递推。只考虑x,发现递推的话,点的顺序是不太重要的,所以可以排好序后一个一个算,假如前i-2个点分别到第i-1个点的距离之和是lst,i和i-1点的距离是dis,那么到第i个点的距离就是 l s t + d i s ∗ ( i − 1 ) lst+dis*(i-1) lst+dis∗(i−1),总贡献就是把所有点的都累加起来就行了,因为两个点直接会产生两次贡献,所以结果乘2。同理y。
不过还有一个问题,颜色个数很多,每个颜色的点的个数也不固定,开静态会爆空间。开动态会比较难写而且慢。考虑因为颜色不会对结果产生影响,所以不需要管颜色具体是什么,我们只需要相同颜色的在一块,并且x或y排好序就行了。
考虑xy分开存储,对x,存储颜色和x值的二元组。然后直接排序就可以了,颜色在前,排好序后相同颜色的就挤在一起了,相同颜色的也用x值排好序了。y同理。
code:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1005;
typedef long long ll;
int n,m;
pair<int,int> x[maxn*maxn],y[maxn*maxn];
int cnt;
int main(){
cin>>n>>m;
for(int i=1,c;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&c);
++cnt;
x[cnt].first=y[cnt].first=c;
x[cnt].second=i;
y[cnt].second=j;
}
sort(x+1,x+cnt+1);
sort(y+1,y+cnt+1);
// for(int i=1;i<=cnt;i++)
// printf("%d %d\n",x[i].first,x[i].second);
ll ans=0;
for(int i=1,wid,lst;i<=cnt;i++){
if(x[i].first!=x[i-1].first)wid=lst=0;
else ans+=lst+=1ll*wid*(x[i].second-x[i-1].second);
wid++;
}
for(int i=1,wid,lst;i<=cnt;i++){
if(y[i].first!=y[i-1].first)wid=lst=0;
else ans+=lst+=1ll*wid*(y[i].second-y[i-1].second);
wid++;
}
cout<<(ans<<1)<<endl;
return 0;
}