【题解】无语凝噎

题目

测试链接:咕咕咕

题目背景

寒蝉凄切,对长亭晚,骤雨初歇。都门帐饮无绪,留恋处,兰舟催发。执手相看泪眼,竟无语凝噎。念去去,千里烟波,暮霭沉沉楚天阔。
多情自古伤离别,更那堪,冷落清秋节!今宵酒醒何处?杨柳岸,晓风残月。 此去经年,应是良辰好景虚设。便纵有千种风情,更与何人说! ——宋·柳永《雨霖铃》

字虽好,只是柳永伤感。执手相看泪眼,竟无语凝噎。太不合此情此景。 ——爱新觉罗氏胤禛

题目描述

西施与范蠡泛舟而去……不对,场景不对,咳咳。在甄嬛前往蓬莱洲之前,她与皇上在碧桐书院告别。为了这可能会长达数月的离别,两个人都似乎有很多话要对对方说,却都无语凝噎。

这时,皇上突然发话:「嬛嬛啊,既然你我都说不出话来,那这时间也不好打发,我们来数三角形吧。」为了满足皇上突发而来的童趣,甄嬛欣然陪同了。可这……

纸上是一张 \(n\times m\) 的格子方阵,即有 \((n+1)\times (m+1)\) 个格点。每个格子都是边长为 \(1\) 的正方形。而他们要数的,则是任取 \(3\) 个格点作为三角形的顶点所形成的 直角三角形 且该三角形面积为 \(\dfrac{s}{2}\) 的个数。甄嬛数得头都晕了,她现在只想知道满足条件的三角形个数 \(\bmod{10^9+7}\)

输入格式

第一行 \(3\) 个正整数 \(n\)\(m\)\(s\),意义如题。

输出格式

仅一个整数,为满足条件的三角形个数 \(\bmod{10^9+7}\)

评测限制

源程序文件名为 wordless.*,其中 * 为所用语言的源程序拓展名。

wordless.in 读入,输出至 wordless.out 中。

评测时间限制 \(1000\ \textrm{ms}\),空间限制 \(128\ \textrm{MiB}\)

数据范围

  • 对于 \(10\%\) 的数据,\(n\le 10\)
  • 对于另外 \(40\%\) 的数据,\(s\) 为质数;
  • 对于 \(100\%\) 的数据,\(1\le n,m,s\le 10^8\)

分析

题目大意是说,给你一个 \(n\times m\) 的点阵,求出所有由格点作为顶点的直角三角形总数。

本题的难点在于,平面格点上不一定只有直角边平行于坐标轴的直角三角形,还有「斜着」的。如何统计这类三角形是重中之重。

\(10\ \texttt{pts}\)

枚举所有组合,检查是否合法即可。

\(50\ \texttt{pts}\)

这种情况比较特别。其实可以分成两类

平着的三角形

显然,即边长只能是一个为 \(1\),另一个为 \(s\)。虽然这并没有什么大作用。

斜着的三角形

这就会比一般情况简单很多,因为只有可能是边长为 \(\sqrt{s}\)等腰直角三角形,易证。

所以,我们只要考虑如何才能统计等腰直角三角形的数量即可。

仿照上面,我们惊奇地发现了两个全等三角形:
t6cV6e.png
我们利用这一点,把这个等腰直角三角形放进一个长方形内,也有 \(4\) 种方案。

这样,这档分就可以了。

\(100\ \texttt{pts}\)

其实,这个图就是我们完成的关键。我们很容易证明,一组等腰三角形必然对应着一个 唯一的 边平行于坐标轴的最小外界长方形。

这个「一组」可能是 \(4\) 个,也有可能是 \(2\) 个,取决于这个等腰三角形的方向。如果两条直角边正好与坐标轴构成了 \(45^{\circ}\) 还等腰就只有俩。

而这个长方形的大小,就需要外面那对 相似三角形 来确定。因为并不一定等腰,所以我们并不能直接得到相似比。


我们设(上面那个图)

\[\large{AB=a,AC=b,\tfrac{\triangle CDE}{\triangle BAC}=k} \]

则易得

\[\large{CD=ka,DE=kb} \]

不妨设 \(k\le 1\),我们就可以很轻松地算出长方形的长和宽。

同时,我们只需要枚举 \(a\)\(b\) 就可以求出所有的三角形。

这就是满分做法的核心部件。

Code

虽然挺烦的,但是锻炼一下码力也是不错的。

#include <cstdio>
#include <cctype>
using namespace std;

typedef long long ll;
const int mod = 1e9 + 7;

inline bool is_int(double b) { return (b - int(b)) < 1e-5; }

inline int read()
{
	int ch = getchar(), t = 1, n = 0;
	while (isspace(ch)) { ch = getchar(); }
	if (ch == '-') { t = -1, ch = getchar(); }
	while (isdigit(ch)) { n = n * 10 + ch - '0', ch = getchar(); }
	return n * t;
}

int main()
{
	ll ans = 0;
	int n, m, s;
	double k;

	scanf("%d%d%d", &n, &m, &s);

	for (int i = 1; i * i <= s; i++)
	{
		if (!(s % i))
		{
			if (i <= n && (s / i) <= m)
				ans = (ans + (ll(n - i + 1) * ll(m - s / i + 1) % mod * 4)) % mod;
			if (i * i != s && i <= m && (s / i) <= n)
				ans = (ans + (ll(n - s / i + 1) * ll(m - i + 1) % mod * 4)) % mod;
		}

		for (int j = i; i * i + j * j <= s; j++)
		{
			k = double(s) / (i * i + j * j);

			if (is_int(k * i) && is_int(k * j))
			{
				if (j + k * i <= n && k * j <= m)
					ans = (ans + (ll(n - j - k * i + 1) * ll(m - k * j + 1)) % mod * (4 - 2 * (i == j))) % mod;
				if (k * j <= n && j + k * i <= m)
					ans = (ans + (ll(m - j - k * i + 1) * ll(n - k * j + 1)) % mod * (4 - 2 * (i == j))) % mod;
			}
		}
	}
	
	printf("%lld\n", ans);


	return 0;
}

后记

一道有意思的模拟题,虽然掺杂了一点数学知识。

做题时,这种题一般很无聊,但做上几题其实也没有什么坏处。

posted @ 2020-06-06 17:48  5ab  阅读(306)  评论(0编辑  收藏  举报