Description
Uthuso 的核反应失控了,她在地灵殿释放了几颗大核弹.地灵殿可以看做一个大小为n*m 的矩阵.一颗大小为k 的核弹,对于任意一个与爆炸中心曼哈顿距离小于k 的地区,会造成(k-(该地区到爆炸中心曼哈顿距离))*(该地区的价值)的损失.现在,地灵殿方面想统计一下每颗核弹造成的损失,请你来帮忙计算.
Input
从flare.in 中读入数据第一行为两个整数n,m接下来n 行每行m 个整数,代表第i 行第j 个区域的价值接下来一行为一个整数Q,代表核弹的数目.接下来Q 行每行三个整数x,y,k,代表第i 颗核弹的爆炸中心以及它的大小.
Output
输出到文件flare.out 中Q 行,每行1 个整数,代表第i 颗核弹的损失.
Sample Input
5 51 2 3 4 59 8 7 6 52 3 3 3 36 6 6 6 61 4 2 8 55
1 1 15 5 13 3 32 2 24 2 2
Sample Output
1
5
833731
Data Constraint
对于30%的数据,满足n<=300,m<=300,Q<=300对于60%的数据,满足n<=300,m<=300对于100%的数据,满足1<=n<=2000,1<=m<=2000,1<=Q<=200000,1<=k<=min(x,y,n-x+1,m-y+1),1<=每个区域的价值<=1000000
Hint
由于本题输入输出量极大,请使用较快的输入输出方式
Solution
这题我的方法比较笨,代码打了100年……
这题是预处理和前缀和的终极体现,想出如何通过前缀和来计算答案十分关键。
对于每个核弹,要计算的答案是一个菱形,而且菱形上的点还要乘上系数,怎么办呢?
考虑将每个菱形分割成四个三角形,对于每个三角形分别统计即可。
对于右上角的三角形(其他同理,这里不做讨论),如上图所示:
设 s[i][j] 表示到第 i 行、第
j 列 的一个锯齿状的价值和(系数全为1),显然有:s[i][j]=s[i−1][j−1]+∑k=1ja[i][k]显然我们可以先做一遍前缀和预处理出 a 矩阵的子矩阵和。
再设 h[i][j] 表示有系数的锯齿阵的价值和(系数就是曼哈顿距离),则有:
h[i][j]=h[i][j−1]+s[i][j]将前一个锯齿阵再覆盖上系数为1的锯齿阵,就能叠起前面的系数。
根据上图,用 h[x][y+k−1]−h[x−k][y−1] 就还剩一个楼梯形,还需要减去一个矩阵。
于是设 g[i][j] 表示从左下角到 (i,j) 的矩阵和(带系数,还是曼哈顿距离),则有:
g[i][j]=g[i+1][j]+左下角到 (i,j) 的矩阵和(系数为1)那个要减的矩阵就等于:
g[x−k+1][y−1]−g[x+1][y−1]∗k(乘上系数)+从左上角到 (x,y−1) 的矩阵和这样我们就能统计出一个三角形的价值和了,其他三角形同理。
总时间复杂度为大常数的 O(N∗M) 。
Code
#include<cstdio>
using namespace std;
const int N=2002;
long long f[N][N],g[N][N],s[N][N],k[N][N];
long long g1[N][N],s1[N][N],k1[N][N];
long long g2[N][N],s2[N][N],k2[N][N];
long long g3[N][N],s3[N][N],k3[N][N];
inline int read()
{
int X=0,w=1; char ch=0;
while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
int main()
{
int n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+read();
s[i][j]=s[i-1][j-1]+f[i][j]-f[i-1][j];
k[i][j]=k[i][j-1]+s[i][j];
}
for(int j=m;j;j--)
for(int i=1;i<=n;i++)
{
s2[i][j]=s2[i-1][j+1]+f[i][j]-f[i][j-1];
k2[i][j]=k2[i-1][j]+s2[i][j];
}
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
{
s3[i][j]=s3[i-1][j-1]+f[i][j]-f[i][j-1];
k3[i][j]=k3[i-1][j]+s3[i][j];
}
for(int i=1;i<=n;i++)
{
long long sum=0;
for(int j=m;j;j--)
{
sum+=f[i][m]-f[i-1][m]-f[i][j-1]+f[i-1][j-1];
g3[i][j]=g3[i-1][j]+f[i-1][m]-f[i-1][j-1]+sum;
}
}
for(int i=1;i<=n;i++)
{
long long sum=0;
for(int j=1;j<=m;j++)
{
sum+=f[i][j]-f[i-1][j];
g2[i][j]=g2[i-1][j]+f[i-1][j]+sum;
}
}
for(int i=1;i<=n;i++)
for(int j=m;j;j--)
{
s1[i][j]=s1[i-1][j+1]+f[i][m]-f[i-1][m]-f[i][j-1]+f[i-1][j-1];
k1[i][j]=k1[i][j+1]+s1[i][j];
}
for(int i=n;i;i--)
{
long long sum=0;
for(int j=1;j<=m;j++)
{
sum+=f[i][j]-f[i-1][j];
g[i][j]=g[i+1][j]+f[n][j]-f[i][j]+sum;
}
}
for(int i=n;i;i--)
{
long long sum=0;
for(int j=m;j;j--)
{
sum+=f[i][m]-f[i-1][m]-f[i][j-1]+f[i-1][j-1];
g1[i][j]=g1[i+1][j]+f[n][m]-f[n][j-1]-f[i][m]+f[i][j-1]+sum;
}
}
int q=read();
while(q--)
{
int x=read(),y=read(),K=read();
long long ans=k[x][y+K-1]-k[x-K][y-1];
long long sum=g[x-K+1][y-1]-g[x+1][y-1]-(f[n][y-1]-f[x][y-1])*K;
sum+=f[x][y-1]-f[x-K][y-1];
ans-=sum;
ans+=k1[x][y-K+1]-k1[x-K+1][y];
sum=g1[x-K+2][y]-g1[x+1][y]-(f[n][m]-f[x][m]-f[n][y-1]+f[x][y-1])*(K-1);
sum+=f[x][m]-f[x-K+1][m]-f[x][y-1]+f[x-K+1][y-1];
ans-=sum;
ans+=k2[x+K-1][y]-k2[x][y+K-1];
sum=g2[x][y+K-2]-g2[x][y-1]-f[x][y-1]*(K-1);
sum+=f[x][y+K-2]-f[x][y-1];
ans-=sum;
ans+=k3[x+K-2][y-1]-k3[x][y-K+1];
sum=g3[x][y-K+2]-g3[x][y]-(f[x][m]-f[x][y-1])*(K-2);
sum+=f[x][y-1]-f[x][y-K+1];
ans-=sum;
printf("%lld\n",ans);
}
return 0;
}