第十四篇:AT_abc476_f
题解补完计划:第十四篇(为什么被打回了?,洛谷护佑我。
原题题面
简要题意
每个格子 \((i,j)\) 的人数为 \((A_i \times B_j) \bmod M\) 。住在单元格 \((s_r,s_c)\) 的一名与会者去单元格 \((t_r,t_c)\) 的咖啡馆的交通费用为 \(\max(|s _ r - t _ r|, |s _ c - t _ c|)\) 。
定义 \(f_{(i, j)}\) 为将所有参与者聚集到位于 \((i, j)\) 小区的咖啡馆所产生的旅行总费用。
求所有的下面的值并异或起来。
- \(f(i,j) + (i-1)N + (j-1)\)
思路
我们先把切比雪夫距离变为曼哈顿距离:
令 \(i_x = \frac{i_r+i_c}{2}, i_y = \frac{i_r-i_c}{2}\)
那么有:
\[\max(|s_r-t_r|,|s_c-t_c|) = |s_x - t_x| + |s_y-t_y|
\]
那么两个点之间的距离就变为横纵坐标之差相加,令 \(g_x\) 表示横坐标为 \(x\) 时距离之和,从 \(g_x\) 到 \(g_{x+1}\),我们发现对于小于 \(x+1\) 的位置距离都增加了 \(1\),而大于等于的位置都减少了 \(1\),考虑维护前缀和 \(sum_i\) 表示横坐标小于等于 \(i\) 的总人数,我们发现 \(g_{x+1} = g_x + sum{x} - (sum_n - sum_x)\),\(O(n)\) 递推即可,对与纵坐标同理,最后加起来求值即可。
总时间复杂度 \(O(n^2)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 1505
#define intl long long
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
intl n,m,a[N],b[N];
intl c[2][N<<1],f[2][N<<1];
void calc(intl k) {
intl len=2*n-1,sum=0,pre=0;
f[k][1]=0;
For(i,1,len) sum += c[k][i], f[k][1] += c[k][i]*(i-1);
For(i,2,len) pre += c[k][i-1], f[k][i] = f[k][i-1]+pre-(sum-pre);
}
int main() {
n=read(),m=read();
For(i,1,n) a[i]=read();
For(i,1,n) b[i]=read();
For(i,1,n) For(j,1,n) {
intl w=a[i]*b[j]%m;
c[0][i+j-1] += w, c[1][i-j+n] += w;
}
calc(0), calc(1);
intl ans = 0;
For(i,1,n) For(j,1,n) ans ^= ((f[0][i+j-1]+f[1][i-j+n])/2+(i-1)*n+(j-1));
printf("%lld\n",ans);
return 0;
}
代码中使用先乘 \(2\) 以避免浮点数误差。

浙公网安备 33010602011771号