您的位置:首页 > 其它

hdu 5514 Frogs 欧拉函数

2017-07-29 22:54 288 查看
题目链接

http://blog.csdn.net/qingshui23/article/details/73091006

力荐这一篇,写得实在是太棒了

题意:

有 n 只青蛙,在环形的 m 块石头上跳,每只青蛙有给定的步长 ai,问所有被青蛙经过的石头的编号的总和

思路:

首先每只青蛙跳跃 k 次后必然会回到起点,

于是有 m | (k * ai),

记 gcd(m, ai) = d, m = q1 * d, ai = q2 * d,

则有 (q1 * d) | (k * q2 * d),

故 q1 | (q2 * k), 其中 (q1, q2) = 1.

故 q1 | k.

取 k = q1 = m / d, 即为回到起点所需的最少步数.

故 可以将青蛙的步长等价为 m / k = d = g
4000
cd(m, ai).

接下来就是原Po的考虑中十分出彩的地方了,

原Po的例子举得都很好,解说也都很具体形象,我这里就来写一些粗浅的证明:

对于每一块石头 x,只考虑步长为 gcd(x, m) 的青蛙(如果存在)对其做出的贡献,这样就能够避免重复计算,

而对任意的 x 考虑 gcd(x, m), 即为考虑所有 m 的因子,

问题就转化成,对每一个 m 的因子 k, 有哪些 x 满足 gcd(x, m) = k.

记 x = d1 * k, m = d2 * k, (d1 < d2)

因为 gcd(x, m) = gcd(d1 * k, d2 * k) = k * gcd(d1, d2) = k,

所以有 gcd(d1, d2) = 1, 其中 d2 = m / k, 

所以 d1 为小于 d2 且与 d2 互质的数,

显然就和欧拉函数联系上了

此外要注意的是,并不是所有 m 的因子都可行,还要再多一个判断,就是 青蛙(等效后的)步长 | 因子k,

这样才保证了编号为 k 的,以及 gcd(x, m) = k 的这些编号为 x 的石头是青蛙可达的。

接着上面的说,要求和,又对 x 求和又怎么办呢?

因为 x = d1 * k, 所以对于特定的 k, 只需要知道 d1 的和,

即 <= d2 = m / k 且与 d2 互质的数的和,

其实这也是有结论的,原Po提到了并且给予了证明

在 [1,x] 中与 x 互素的数的和为: Phi(x)∗x2 

详情请看原Po博文

这样,这道题就通了

再次力荐原Po的博文,写的真的很棒

http://blog.csdn.net/qingshui23/article/details/73091006

超级丰富详细,看得小垃圾我很是感动qwqq

还有就是一些细节上的处理,

比如说

1. 本身就有步长与 m 互质的青蛙,那么必然每块石头都会经过

2. 对于等效后的步长用 unique 函数进行去重,减少重复计算

(都是跟原Po学的qwq太感谢惹)

AC代码如下:

#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxn 10010
typedef long long LL;
using namespace std;
LL n, m, a[maxn], b[maxn];
int kas;
LL gcd(LL a, LL b) {
if (!b) return a;
return gcd(b, a % b);
}
LL phi(LL x) {
LL ans = x;
for (LL i = 2; i * i <= x; ++i) {
if (x % i == 0) {
ans -= ans / i;
while (x % i == 0) x /= i;
}
}
if (x > 1) ans -= ans / x;
return ans;
}
void work() {
scanf("%lld%lld", &n, &m);
bool flag = false;
for (int i = 0; i < n; ++i) {
scanf("%lld", &a[i]);
a[i] = gcd(a[i], m);
if (a[i] == 1) flag = true;
}
if (flag) { printf("Case #%d: %lld\n", ++kas, m * (m - 1) >> 1); return; }
sort(a, a + n);
n = unique(a, a + n) - a;
int tot = 0;
for (LL i = 2; i * i <= m; ++i) {
if (i * i == m) { b[tot++] = i; break; }
if (m % i == 0) {
b[tot++] = i;
b[tot++] = m / i;
}
}
LL ans = 0;
for (int i = 0; i < tot; ++i) {
for (int j = 0; j < n; ++j) {
if (a[j] > b[i]) break;
if (b[i] % a[j] == 0) {
//                printf("%lld\n", b[i]);
ans += phi(m / b[i]);
break;
}
}
}
ans = ans * m / 2;
printf("Case #%d: %lld\n", ++kas, ans);
}
int main() {
freopen("5514.in", "r", stdin);
int T;
scanf("%d", &T);
while (T--) work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: