题意:
有一个n*m的方格,每一格有a[i,j]个星。
现在每次可以取同一行或同一列的两颗星,让他们向中心移动一格,并且获得他们中间隔的区域数个能量。
给定最终状态,问最多可能获得的能量。
做法:
首先“最多”这个东西是来忽悠你的。
发现获得的能量是一定的。行列独立。
然后我们来推一波式子。
假设有一行是这样的:
1 0 0 0 0 … 0 0 1
最左边的左边为(x,y),最右边的坐标为(x,y+n-1)。中间相隔n-2个区域。
移动一格后,变成:
0 1 0 0 0 … 0 1 0
新的两个格子变成(x,y+1),(x,y+n-2)。
(x^2+y^2+x^2+(y+n-1)^2)-(x^+(y+1)^2+x^2+(y+n-2)^2)这个式子展开化简得到
=2n-4=2(n-2)
把它除以2就是获得的能量。
于是我们得到了结论:
所有格子的星星数量乘行列的平方和,初始和最终状态相减就是答案。
然后这个东西究竟为什么是这样很玄学,黄学长博客上是这么写的,大概涉及到了物理知识qaq:
一个星星的势能定义为他到左上角格子的距离的平方(即,行号的平方和列号的平方的和),可知,使用一次魔法释放出的魔力就是两个星星的势能和的改变量/2。这样,计算初、末状态的所有星星的势能和,相减/2即可。”
(怎么感觉跟没说差不多)
易错点:
注意开long long。
代码:
/*************************************************************
Problem: bzoj 2321 [BeiJing2011集训]星器
User: fengyuan
Language: C++
Result: Accepted
Time: 52 ms
Memory: 1288 kb
Submit_Time: 2018-01-17 14:07:35
*************************************************************/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cctype>
using namespace std;
typedef long long ll;
int n, m;
ll ans, x;
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++) {
scanf("%lld", &x);
ans += x*(i*i+j*j);
}
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++) {
scanf("%lld", &x);
ans -= x*(i*i+j*j);
}
printf("%lld\n", ans/2);
return 0;
}