CodeForces 215C 题解
题解笔记
CF215C Crosses(cross.cpp)
时间限制 \(2s\) | 内存限制 \(256M\)
题目描述:
有一个 n×m 的矩阵,我们用一个六元组 \((a,b,c,d,x_0,y_0)\) 表示一个十字形:
若一个方格 (x,y) 满足下面两个条件之一,则这个方格属于这个六元组表示的十字形
- $ \mid x−x_0 \mid \leq a $ 且 $ \mid y−y_0 \mid \leq b $
- $ \mid x−x_0 \mid \leq c $ 且 $ \mid y−y_0 \mid \leq d $
已知一个十字形的面积是 s,问有多少个六元组满足要求。
一个十字型如下图所示:

输入格式:
输入由一行包含三个整数 \(n , m\) 和 \(s\) ( \(1 \leq n,m \leq 500\) , \(1 \leq s \leq nm\) ) 。整数之间用空格分隔。
输出格式:
输出一个整数表示满足题目条件六元组数量。
输入输出样例:
| 样例1输入 | 样例1输出 | 样例2输入 | 样例2输出 |
|---|---|---|---|
| 2 2 1 | 4 | 3 4 5 | 4 |
样例解释:
在第一个样例中,满足要求的六元组是:$ ( 0 , 0 , 0 , 0 , 1 , 1 ) , ( 0 , 0 , 0 , 0 , 1 , 2 ) , ( 0 , 0 , 0 , 0 , 2 , 1 ) , ( 0 , 0 , 0 , 0 , 2 , 2 ) $ 。
在第二个样例中,满足要求的六元组是:$( 0 , 1 , 1 , 0 , 2 , 2 ) , ( 0 , 1 , 1 , 0 , 2 , 3 ) , ( 1 , 0 , 0 , 1 , 2 , 2 ) , ( 1 , 0 , 0 , 1 , 2 , 3 ) $ 。
解题思路:
下文的第一张图借用了 这篇题解 的图片( 事实上,我是看了这篇题解才 AC 了这道题 )
理解题意
下图这张是 \((2,1,0,2,5,5)\) 表示的十字,可以发现,这个十字形是由两个矩形重叠组成的,即红蓝两个矩形

中间的黑格就是 \((5,5)\),十字形的六元组。
用过这张图就大概能完美理解题意
开始正式题解
题解从这里开始
直接枚举六元组肯定是超时的
但是由于矩形也是属于十字形的,所以可以枚举矩形,根据矩形的面积可以分为如下几种情况( \(x,y\) 是矩形的长和宽 ):
- \(xy=s\)
计算公式:
但是对于每一个矩形,计算出了六元组中 \(a,b\) 的值,但是 \(c,d\) 的值还有很多种可能,即 \((i+1) \div 2 \times ( j+2)\) 种可能
- \(xy<s\)
那对于面积不够大的情况,我们尝试在上下左右补上一部分面积,但是由于十字形是对称的,所以需要补的点应该是一个偶数,即 \(s-xy\) 应该是偶数
那怎么补?
这里并不需要在左右补,只需要在上下补即可,因为如果存在 \(x \times y\) 的矩阵使得在左右补可行的话,如下图:

左右补之后如下图示:

但为什么不能将看成一个细长的矩形(即下图的红矩形)在上下添加了一些方块(下图的蓝方块)呢?

因此只需考虑在上下添加方块就行了,由于是对称的将上下添加的部分合在一起,构成一个矩形,枚举这个矩形的长 \(k\) ,
由于 \(k\) 是长且 \(s-xy\) 是面积,所以 \(s-xy\) 一定能被 \(k\) 整除
但补后的十字是不能超过整个网格的宽的,所以 \((s-xy) \div k+x < n\)
最后一点优化
最后可以发现,所谓通过 $ \mid x−x_0 \mid \leq a $ 且 $ \mid y−y_0 \mid \leq b $ 的条件画出一个矩阵,其实是由上下左右四个矩阵构成了一个大矩阵,如下图:

四个矩形重叠的地方就是\((x_0,y_0)\) ,即中心点
而这四个矩形都是 \(a \times b\) 的大小,
所以由这张图就可以看出,矩形的长和宽一定都是奇数,因此在补方块时的矩形的长 \(k\) 一定也是奇数
参考代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m,s;
ll ans;
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1; i<=n; i+=2)
{
for(int j=1; j<=m; j+=2)
{
int lst=s-i*j;
if(lst==0) ans+=(2ll*((i-1)/2+1)*((j-1)/2+1)-1)*(n-i+1)*(m-j+1);
else if(lst>0&&!(lst&1))
{
for(int k=1; k<j; k+=2)
{
if(!(lst%k)&&i+lst/k<=n) ans+=2ll*(n-i-lst/k+1)*(m-j+1);
}
}
}
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号