[NOI online2022提高C] 如何正确地排序

发布日期:2026-06-13 00:56:58 分类:wwwBet365 浏览:5785

题目描述

有一个 \(m\times n\) 的数组 \(a_{i,j}\)。

定义:

\(f(i,j)=\min\limits_{k=1}^m(a_{k,i}+a_{k,j})+\max\limits_{k=1}^m(a_{k,i}+a_{k,j})\)

你需要求出 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^nf(i,j)\)

输入格式

第一行两个正整数 \(m,n\)。

接下来 \(m\) 行,每行 \(n\) 个正整数表示 \(a_{i,j}\)。

输出格式

一行一个正整数,表示答案。

输入输出样例

输入 #1

3 5

1 7 2 2 7

9 10 4 10 3

7 7 8 10 2

输出 #1复制

564

说明/提示

【样例 1 解释】

以 \(f(3,5)\) 为例:

\(\begin{aligned}f(3,5)&=\max(a_{1,3}+a_{1,5},a_{2,3}+a_{2,5},a_{3,3}+a_{3,5})+\min(a_{1,3}+a_{1,5},a_{2,3}+a_{2,5},a_{3,3}+a_{3,5})\\&=\max(9,7,10)+\min(9,7,10)\\&=10+7\\&=17\end{aligned}\)

下面给出 \(f(i,j)\) 的数表,第 \(i\) 行第 \(j\) 列表示 \(f(i,j)\):

\(\begin{array}{|c|c|c|c|c|}\hline20&27&18&22&20\\\hline27&34&24&29&23\\\hline18&24&20&22&17\\\hline22&29&22&24&22\\\hline20&23&17&22&18\\\hline\end{array}\)

它们的和是答案 \(564\)。

统一一下,为了避免繁琐的分类讨论,我们把第一行多复制几次,填满四行。这样子答案很明显是不会变的,本来的最大值和最小值不会变。所以我们同意\(m=4\)处理。

首先我们考虑计算出一个数\(a_{i,j}\)会被计算多少次,然后乘上。但是这样子计算需要三维偏序,代码量巨大,同时容易被卡常。所以我们反着来考虑,设他在每次涉及这一列时都会被计算一次,那么总答案为\(2\times n\times sum\),sum为\(a_{i,j}\)的和。要注意\(f(i,j)\)和\(f(j,i)\)都会被计算到。

然后考虑一个数什么时候是没有贡献的,当存在$$a_i+a_j\le b_i+b_j\le c_i+c_j$$时,\(b_j\)在计算\(f(i,j)\)时没有贡献。我们可以枚举a,b,c数组然后减去b的在上面这种情况中的不合法现象。

如何计算是一个重点。首先对式子进行移项,得到$$a_i-b_i\le b_j-a_j$$

\[b_i-c_i\le c_j-b_j

\]

对于j这个位置来说,要求有多少个数是满足上面两个式子的,这很明显是一个二位偏序问题。我们把所有\(a_i-b_i\)和$ b_j-a_j$进行排序,然后遍历。如果是前者那就更新,如果是后者那就询问。题目为了帮我们卡常,很良心地把范围缩小。

其实大体就是这样了。注意其实一个不在范围里的数他也会被减去两次,设\(a

还有一个值得注意的地方,设\(a

#include

using namespace std;

const int N=2e5+5;

int m,n,a[5][N],tr[N+N],lsh[N+N],ret,mx,mn,t;

long long sum;

struct node{

int x,y,val,z;

bool operator<(const node&n)const{

if(x!=n.x)

return x

return z

}

}q[N+N];

int ask(int x)

{

ret=0;

for(;x;x-=x&-x)

ret+=tr[x];

return ret;

}

void update(int x)

{

for(;x<=N+N;x+=x&-x)

tr[x]++;

}

void solve(int x,int y,int z)

{

memset(tr,0,sizeof(tr));

for(int i=1;i<=n;i++)

{

q[i<<1]=(node){a[y][i]-a[x][i],a[z][i]-a[y][i],a[y][i],1};

q[2*i-1]=(node){a[x][i]-a[y][i]+(x>y),a[y][i]-a[z][i]+(y>z),0,0};

}

sort(q+1,q+n+n+1);

for(int i=1;i<=n+n;i++)

{

if(q[i].z)

sum-=1LL*q[i].val*ask(q[i].y+N);

else

update(q[i].y+N);

}

}

int main()

{

scanf("%d%d",&m,&n);

for(int i=1;i<=m;i++)

for(int j=1;j<=n;j++)

scanf("%d",a[i]+j);

for(int i=m+1;i<=4;i++)

for(int j=1;j<=n;j++)

a[i][j]=a[1][j];

for(int i=1;i<=4;i++)

for(int j=1;j<=n;j++)

sum+=a[i][j];

sum=sum*n*2;

for(int i=1;i<=4;i++)

for(int j=1;j<=4;j++)

if(i!=j)

for(int k=1;k<=4;k++)

if(i!=k&&j!=k)

solve(i,j,k);

printf("%lld",sum);

return 0;

}