P1861 星之器
星之器
题目背景
Magic Land 上的时间又过了若干世纪…
现在,人们谈论着一个传说:从前,他们的祖先来到了一个位于东方的岛屿,那里简直就是另外一个世界。善于分析与构造的 Magic Land 上的人们总是不明白那里的人们是如何不借助精确的实验与计算驱动和操纵魔法。
题目描述
偶然地,一个魔法使(Magician)来到了 Magic Land,在临走的时候留下了一个神奇的盒子,叫做星之器(Casket of star)。
虽然不知道这个盒子是做什么的,但是经过了大量的实验和计算后,人们已经清楚它的一些事实:
- 星之器之中有 \(N\times M\) 个区域,可看作分成 \(N\) 行和 \(M\) 列的格子,每个区域之中有若干单位的称为“星”的对象,这个对象的最小单位已经被确定,所以,这个数量总是整数。
- 魔法使可以驱动星之器中位于同一行或同一列的不相邻(有公共边的区域称为相邻的)两个区域中各 \(1\) 单位的“星”,使得它们分别向中心移动 \(1\) 格。
- 每一次使用 2 中的方法驱动“星”,将会产生魔力,魔法使会得到这一部分魔力。魔力的量等于这个两个区域之间所间隔的区域数。
这样,我们可以用一个 \(N\times M\) 的数表来表示星之器的状态,比如 \(N=2,M=3\) 时:
2 0 1 1 2 0
5 1 4 5 1 4
当星之器为左侧的状态时,通过操纵第一行的第 \(1\) 和 \(3\) 个区域中的“星”,变为右侧所示的状态,同时,将产生 \(1\) 单位的魔力(因为这两个区域之间恰好隔了 \(1\) 个区域)。
在经过了进一步的研究之后,人们知道了这个星之器最初的状态(Ini)以及最终被他们得到时的状态(Fin)。
你希望知道,星之器最多帮助它的拥有者提供了多少的魔力。即:经过一系列上述操作由初态(Ini)变为终态(Fin),至多产生多少魔力。
需要注意的是,显然操作过程中每个区域内“星”的数量不能是负的,即:如果那个区域已经没有“星”了,当然就不能继续操作了。
输入格式
第一行包含两个正整数 \(N,M\) 表示星之器的大小。
接下来的 \(N\) 行,每行包含 \(M\) 个自然数:\(\mathit{Ini}_{i,j}\),描绘了初态(Ini)。
在一个空行后的 \(N\) 行,每行包含 \(M\) 个自然数:\(\mathit{Fin}_{i,j}\),描绘了终态(Fin)。
输出格式
输出一个正整数,表示至多产生的魔力。
样例 #1
样例输入 #1
5 5
1 0 0 0 1
0 0 0 0 0
0 0 0 0 0
0 1 0 1 1
1 0 0 0 0
0 0 0 0 0
0 0 0 0 1
2 0 0 0 1
0 0 2 0 0
0 0 0 0 0
样例输出 #1
7
提示
对于 \(100\%\) 的数据,\(1\le N,M\le200\),\(\mathit{Ini}_{i,j},\mathit{Fin}_{i,j}\le1000\)。
所有数据保证了至少存在一个操作方法使得星之器由初态变为终态,同时保证了初态与终态不是完全相同的。
Solution
神题,不接受任何反驳。
先将问题给压缩成为一维,不难发现,产生的魔力只与初态和终态相关,而与中间过程无关。在物理学上,有一个东西叫做重力势能,如果要知道物体从某处下落至另一处,只需要知道起点的高度和终点的高度,即可算出相差的重力势能,而与中间下落的过程无关。试着将这两个东西联想到一起,为每一个点设置一个势能函数 \(f(x)\),使得我们能够通过势能的相减得出产生魔力的值。
假设需要将两个点 \(x,y\) 给进行一次变换(不妨设 \(x<y\)),那么根据势能函数需要完成的事情,可以推导出这个式子:
进行移项得:
不难发现,等号左右侧的式子形式是相同的,所以可以设这个等号等于一个定值 \(C\),即需要找到一个表达式满足:
因为表达式的形式对答案没有影响,所以为了方便推导,将 \(C\) 直接假设成为 \(0\)。那么有:
很明显,这个递推式的通项公式就是一个二次函数,所以将这个二次函数的一次项和常数项全部假设为 \(0\),表示成 \(f(x)=ax^2\),将 \(x=1\) 带入可以解得 \(\displaystyle a=\frac{1}{2}\),因此势能函数即为 \(\displaystyle f(x)=\frac{x^2}{2}\)。
如果 \(x\) 位置上的“星”的数量为 \(cnt_x\),那么不难发现,这个点的势能就应该是 \(cnt_x\times f(x)\)(每个“星”都可以进行变换,所以这个点可以变换的总势能就应该是单个“星”的势能乘以“星”的个数)。
尝试推广到二维。首先,二维的情况下的答案也只与初态和终态有关,与过程无关,所以可以将我们上面的势能函数推广到二维:
随便带一组数据进去验证,可以发现是正确的。那么答案就应该是初态矩阵的势能之和减去终态矩阵的势能之和:
Code
代码实现中甚至可以不用开数组,并且计算 \(st\) 和 \(ed\) 的时候势能函数可以先不除以 \(2\),在最终答案的地方除以 \(2\) 即可。
#include<bits/stdc++.h>
using namespace std;
template<typename T> void read(T &k)
{
k=0;T flag=1;char b=getchar();
while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
while (isdigit(b)) {k=k*10+b-48;b=getchar();}
k*=flag;
}
template<typename T> void write(T k) {if (k<0) putchar('-'),write(-k);if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
int n,m,x;
long long st=0,ed=0;
int main()
{
read(n),read(m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
read(x),st+=(i*i+j*j)*x;//初态
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
read(x),ed+=(i*i+j*j)*x;//终态
writewith((st-ed)>>1,'\n');//答案为(st-ed)/2
return 0;
}