题意:
给一个R行C列的表格以及MinT,MaxT。
要求选三个点(无标号,两两不同行、列),使得它们两两的Manhattan距离和在[MinT,MaxT]范围内。
求有多少种不同的选择(对1e9+7取模)。
3 ≤ R , C ≤ 4 ∗ 1 0 3 3\le R,C\le4*10^3 3≤R,C≤4∗103, 1 ≤ M i n T ≤ M a x T ≤ 2 ∗ 1 0 4 1\le MinT\le MaxT\le 2*10^4 1≤MinT≤MaxT≤2∗104。
先把单元格抽象为格点。
显然地,
∑
d
i
s
M
a
n
h
a
t
t
a
n
=
2
×
(
m
a
x
{
x
}
−
m
i
n
{
x
}
+
m
a
x
{
y
}
−
m
i
n
{
y
}
)
\sum dis_{Manhattan}=2×(max\{x\}-min\{x\}+max\{y\}-min\{y\})
∑disManhattan=2×(max{x}−min{x}+max{y}−min{y})。
也就是长方形的周长。如果不要求不同行、列,还可能是线。
枚举某个顶点,以这个点作为左上角顶点的长方形的长、宽。
- 另外两个点分别在不以这个点为端点的两条边上。
- 或者有一个是这个点的对角顶点,另外一个在中间的长方形里边。
其中第一种情况要乘上四个方向,第二种情况要乘二
并且发现第一种情况和第二种本质相同,贡献都是 长减掉一 乘 宽减掉一
枚举所有情况(用
M
i
n
T
MinT
MinT和
M
a
x
T
MaxT
MaxT限制一下),然后枚举点加上。
这道题就没了。
一开始把它分了四块,算了半天没写出来代码(
#include <cstdio>
#include <iostream>
using namespace std;
const long long MOD = 1000000007ll;
long long Ans = 0,List[4005][4005] = {};
int Row,MinT,MaxT,Column;
int main () {
#ifdef Judge
freopen("table.in", "r", stdin);
freopen("table.out", "w", stdout);
#endif
scanf("%d%d%d%d",&Row,&Column,&MinT,&MaxT);
MinT>>=1,MaxT>>=1;
if((MaxT<4)||(Row<3)||(Column<3)) { printf("0"); return 0;}
for(register int i=2;i<=Row-1;++i) {
for(register int j=2;j<=Column-1;++j) {
List[i][j]=(List[i-1][j]-List[i-1][j-1]+List[i][j-1]+MOD)%MOD;
if(i+j>=MinT&&i+j<=MaxT)List[i][j]+=(i-1)*(j-1)%MOD;
List[i][j]%=MOD;
}
}
for(register int i=3;i<=Row;++i) {
for(register int j=3;j<=Column;++j) {
Ans+=List[i-1][j-1]%MOD;
}
}
printf("%lld",Ans*6%MOD);
return 0;
}
实际上如果用枚举矩阵的思路会更好做,乘上图中该矩阵数量就好了
#include <cstdio>
#include <iostream>
using namespace std;
const long long MOD = 1000000007ll;
long long Ans = 0,List[4005][4005] = {};
int Row,MinT,MaxT,Column;
int main () {
#ifdef Judge
freopen("table.in", "r", stdin);
freopen("table.out", "w", stdout);
#endif
scanf("%d%d%d%d",&Row,&Column,&MinT,&MaxT);
MinT=MinT+1>>1,MaxT=MaxT>>1;
for(register int i=2;i<=Row-1;++i)
for(register int j=2;j<=Column-1;++j)
if(i+j-1>=MinT&&i+j-1<=MaxT)
Ans=(Ans+(i-1)*(j-1)*(Row-i)*(Column-j))%MOD;
printf("%lld",Ans*6%MOD);
return 0;
}
为我的智商默哀(