HDU 5514 Frogs 欧拉函数
2015-11-05 17:02
330 查看
题意:
有\(m(1 \leq m \leq 10^9)\)个石子排成一圈,编号分别为\(0,1,2 \cdots m-1\)。现在在\(0\)号石头上有\(n(1 \leq n \leq 10^4)\)只青蛙。第\(i\)只青蛙每次能往前跳\(a_i\)步,但是他们跳的次数不加限制。
如果一块石头能至少被一只青蛙跳上去,那么称这块石头被占领了。
求所有可能被占领的石头的编号和。
分析:
首先我们应该发现这样一个事实:每次向前跳\(a_i\)步的效果和跳\(GCD(a_i, m)\)步是一样的
所以可以对每个\(a_i\)和\(m\)求个\(GCD\),然后排序去重,这样\(a_i\)的数量就会很少。
在第一个样例中,\(m=12\),求完\(GCD\)的\(a_i\)为\((2, 3)\)。
那么被\(2\)占领的石块的编号为\((0, 2, 4, 6, 8, 10)\);被\(3\)占领的石块的编号为\((0, 3, 6, 9)\)。
显然这样是有重复的,\(6\)号石块既会被\(2\)占领,又会被\(3\)占领。
和HDU 5528 Count a * b这题一样,为了避免算重,我们规定:
编号为\(i\)的石块只会被步长为\(GCD(i, m)\)的青蛙占领
在这之前我们还要预处理一下:
找出\(m\)所有的约数,然后用一个标记数组标记这些约数中存在哪些步长的青蛙。还要把所有步长的倍数也都标记上,也就是说有一个步长为\(d\)的青蛙,如果\(kd\)也是\(m\)的约数,我们可以假设还有一只步长为\(kd\)的青蛙存在。因为这样并不影响最终的结果。
还是上面那个例子,对于步长为\(2\)的青蛙,我们只统计\((2,10)\)这两个石块。其他的\((4,8)\)这两个石块会在步长为\(4\)的青蛙中统计,\((6)\)这个石块会在步长为\(6\)的青蛙中统计上。
一般地,对于步长为\(d\)的青蛙,我们要统计的石块的个数就是\([1,\frac{m}{d}]\)互质的个数,即\(\phi(\frac{m}{d})\)。
这些石块的编号和为\(d \cdot \frac{\frac{m}{d} \phi(\frac{m}{d})}{2}=m\frac{\phi(\frac{m}{d})}{2}\)。
解释一下上面公式:
其实我们只需要证明这样一条公式:
\(n > 1\)时,\([1,n]\)中与\(n\)互质的数字之和为:\(\frac{n \phi(n)}{2}\)
证明:
因为\(GCD(n,i)=GCD(n,n-i)\),所以如果有\(GCD(n,i)=1\),那么也一定有\(GCD(n,n-i)=1\)成立。
所以满足\(GCD(n,i)=1\)的数都是成对出现的,而且它俩的和为\(n\)。
还要证明一下,这样不会算重,也就是不会出现\(i=n-i\)的情况。
因为如果\(i=n-i \Rightarrow n=2i\),得到\(n\)是偶数,\(i=\frac{n}{2}\)。显然\(GCD(n,i) \neq 1\),推出矛盾。
上面是针对\(n>2\)的情况,幸运的是,\(n=2\)也满足这条公式。
证毕。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <map> #define MP make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; const int maxp = 32000; int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } int pcnt; int prime[maxp], phi[maxp]; bool vis[maxp]; void preprocess() { for(int i = 2; i < maxp; i++) { if(!vis[i]) { prime[pcnt++] = i; phi[i] = i - 1; } for(int j = 0; j < pcnt && i * prime[j] < maxp; j++) { vis[i * prime[j]] = true; if(i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } else phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } } LL Phi(int n) { if(n < maxp) return phi ; LL ans = 1; for(int i = 0; i < pcnt; i++) { if(prime[i] * prime[i] > n) break; for(int j = 0; n % prime[i] == 0; j++) { if(!j) ans *= (prime[i] - 1); else ans *= prime[i]; n /= prime[i]; } } if(n > 1) ans *= (n - 1); return ans; } const int maxn = 10000 + 10; int n, m; int a[maxn]; vector<PII> divide; vector<int> factors; bool occupied[maxn]; void dfs(int d, int x) { if(d == divide.size()) { factors.push_back(x); return ; } int p = divide[d].first, e = divide[d].second; for(int i = 0; i <= e; i++) { dfs(d+1, x); x *= p; } } int main() { preprocess(); int T; scanf("%d", &T); for(int kase = 1; kase <= T; kase++) { printf("Case #%d: ", kase); scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) { scanf("%d", a + i); a[i] = gcd(m, a[i]); } sort(a, a + n); n = unique(a, a + n) - a; if(a[0] == 1) { printf("%lld\n", (LL)m * (m-1) / 2); continue; } divide.clear(); int t = m; for(int i = 0; i < pcnt; i++) { if(prime[i] * prime[i] > t) break; if(t % prime[i] == 0) { int cnt = 0; while(t % prime[i] == 0) { t /= prime[i]; cnt++; } divide.push_back(MP(prime[i], cnt)); } } if(t > 1) divide.push_back(MP(t, 1)); factors.clear(); dfs(0, 1); sort(factors.begin(), factors.end()); int sz = factors.size(); memset(occupied, false, sizeof(occupied)); for(int i = 0; i < n; i++) { int k = lower_bound(factors.begin(), factors.end(), a[i]) - factors.begin(); occupied[k] = true; } for(int i = 1; i < sz; i++) if(!occupied[i]) for(int j = 0; j < i; j++) if(factors[i] % factors[j] == 0 && occupied[j]) { occupied[i] = true; break; } LL ans = 0; for(int i = 0; i < sz - 1; i++) if(occupied[i]) { int t = m / factors[i]; ans += (LL)m * Phi(t) / 2; } printf("%lld\n", ans); } return 0; }
相关文章推荐
- 窗口和对话框居中对齐
- 元组
- [C#]文本处理的常用函数(TextProcess)
- 你真的真的理解Java的按引用传递吗?
- 关于MATLAB函数不可用问题。??? Undefined function or method 'minfo' for input arguments of type 'double'.
- java常用类库之数组操作类
- C#中的 int?是什么意思
- 服务器的发送数据
- css div的宽度随内部元素的宽度的变化而变化
- Oracle学习记录之使用自定义函数和触发器实现主键动态生成
- 手机 app 开发的几种方式 ionic 学习思路以及 ionic 新建的项目分析
- AlarmManager用法
- SQL Server MSSQLSERVER 服务无法启动解决方案
- Android ListView 实现Item单选效果
- 利用java集合模拟斗地主(发牌)
- 黑马程序员——JAVA基础----语法(一)-- 关键字、常量、变量、运算符
- Linux常用命令
- IOS 自定义转场动画
- Daily Scrum 11.5
- 继SaaS后的下一代主要技术猜想