[NOIP2012模拟10.25] 单元格 [构造]

题意:
给一个R行C列的表格以及MinT,MaxT。
要求选三个点(无标号,两两不同行、列),使得它们两两的Manhattan距离和在[MinT,MaxT]范围内。
求有多少种不同的选择(对1e9+7取模)。
3 ≤ R , C ≤ 4 ∗ 1 0 3 3\le R,C\le4*10^3 3R,C4103 1 ≤ M i n T ≤ M a x T ≤ 2 ∗ 1 0 4 1\le MinT\le MaxT\le 2*10^4 1MinTMaxT2104

先把单元格抽象为格点。

显然地, ∑ 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})
也就是长方形的周长。如果不要求不同行、列,还可能是线。

枚举某个顶点,以这个点作为左上角顶点的长方形的长、宽。

  1. 另外两个点分别在不以这个点为端点的两条边上。
  2. 或者有一个是这个点的对角顶点,另外一个在中间的长方形里边。

其中第一种情况要乘上四个方向,第二种情况要乘二
并且发现第一种情况和第二种本质相同,贡献都是 长减掉一 乘 宽减掉一
枚举所有情况(用 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;
}

为我的智商默哀(

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值