HDU - 5514 Frogs 欧拉函数||容斥定理
2017-09-28 00:14
351 查看
题目链接
题意:
有 n 个青蛙,第 i 个青蛙每次只能够跳 ai 步,现在有 m 个石头围成一圈,编号为 0 到 m−1,现在青蛙可以围着这个石头组成的圆跳无限次,每跳一次就会占领这个石头,可以无限占领,现在问你的是这 n 个青蛙占领的石头的编号的总和是多少。
思路:
先说第一种方法:
我们可以发现对于每个ai,他所能经过的石头为 k*gcd(m,ai).但是我们发现比如第一个样例
2 12
9 10 ,gcd分别为2 ,3。
2 经过 0 2 4 6 8 10
3经过 0 3 6 9
这就会有重复的,比如6就会被算两次.这就需要去掉了,这时候就可以想到这个题需要容斥一下了.那么关键是怎么容斥?
一开始我想错了,因为发现很大,我想用map存一下所有的gcd然后暴力枚举来着,再倒着容斥.然后T了.
这个题有个容斥的技巧就是预处理出所有m的因子然后进行容斥,可以发现所有被重复计算的值以及所有的gcd一定是m的因子(重复的一定是几个数的LCM).并且int范围内每个数因子的个数最多1600+。首先对于m的一个因子g,如果这个g满足题意,那么m的其他因子并且是k*g的一定被重复计算了,我们需要去掉.g对答案的贡献为(mg−1)∗m2 (等差数列求和). 上面说的公式计算在没有重复计算的情况下的,对于重复计算的根据容斥原理,开一个数组num[i]表示m的第i个因子被加到总答案中几次.每个答案都只能被计算一次,多计算的我们就需要减掉多加的,多减的同样. 这样计算公式为:num[i]∗(mg−1)∗m2
另外对于每个gcd(ai,m) 需要将每个gcd的倍数初始置1,并且每次从小到大计算答案时,需要对第i个gcd的所有倍数的num数组进行更新.(容斥原理本质),因为上面说过因子不会很多,所以直接暴力枚举即可.
第二种做法就是欧拉函数,我觉得很巧妙。
还是和上面一样,每次都是经过k*gcd(ai,m)的格子,对于重复计算的我们规定,第i块石头只能由gcd = gcd(i,m) 的青蛙经过.(如果不存在这样的青蛙我们假设添加一个这样的青蛙,对结果的求和是没有影响的).
对于样例可以得出如下结论:
2 12
9 10
步数为2 的经过 2 10
步数为3 的经过 3 9
步数为4的经过 4 8
步数为6的经过 6 . 综上就消除了所有重复的情况.
他们每个对答案的贡献:
2*(1 +5),3*(1+3),4*(1+2),6*(1) 可以发现一个神奇的东西,对于每个x,他的贡献为 x*(与mx互质的所有数的和).
存在一个结论:
[1,x]中与x互质的所有的数和为 φ(x)∗x2 ,知道这个结论这个题目就简单了.
预处理m的所有因子,然后记录所有gcd(ai,m)。然后枚举每个m的因子,先判断该石头能否被走过,(即是否有gcd可以整除该因子).如果被走过就按照公式暴力计算就好了.
题意:
有 n 个青蛙,第 i 个青蛙每次只能够跳 ai 步,现在有 m 个石头围成一圈,编号为 0 到 m−1,现在青蛙可以围着这个石头组成的圆跳无限次,每跳一次就会占领这个石头,可以无限占领,现在问你的是这 n 个青蛙占领的石头的编号的总和是多少。
思路:
先说第一种方法:
我们可以发现对于每个ai,他所能经过的石头为 k*gcd(m,ai).但是我们发现比如第一个样例
2 12
9 10 ,gcd分别为2 ,3。
2 经过 0 2 4 6 8 10
3经过 0 3 6 9
这就会有重复的,比如6就会被算两次.这就需要去掉了,这时候就可以想到这个题需要容斥一下了.那么关键是怎么容斥?
一开始我想错了,因为发现很大,我想用map存一下所有的gcd然后暴力枚举来着,再倒着容斥.然后T了.
这个题有个容斥的技巧就是预处理出所有m的因子然后进行容斥,可以发现所有被重复计算的值以及所有的gcd一定是m的因子(重复的一定是几个数的LCM).并且int范围内每个数因子的个数最多1600+。首先对于m的一个因子g,如果这个g满足题意,那么m的其他因子并且是k*g的一定被重复计算了,我们需要去掉.g对答案的贡献为(mg−1)∗m2 (等差数列求和). 上面说的公式计算在没有重复计算的情况下的,对于重复计算的根据容斥原理,开一个数组num[i]表示m的第i个因子被加到总答案中几次.每个答案都只能被计算一次,多计算的我们就需要减掉多加的,多减的同样. 这样计算公式为:num[i]∗(mg−1)∗m2
另外对于每个gcd(ai,m) 需要将每个gcd的倍数初始置1,并且每次从小到大计算答案时,需要对第i个gcd的所有倍数的num数组进行更新.(容斥原理本质),因为上面说过因子不会很多,所以直接暴力枚举即可.
#include<bits/stdc++.h> #define pb push_back using namespace std; const int maxn = 1e4 + 5; typedef long long ll; int a,num[maxn]; int n,m; vector<int>g; int main() { int _; cin>>_; int ca = 1; while(_--) { g.clear(); memset(num,0,sizeof num); scanf("%d %d",&n,&m); bool flag = 0; for(int i = 1;i * i <= m;++i) { if(m % i == 0) { g.pb(i); if(i != m/i) g.pb(m/i); } } sort(g.begin(),g.end()); for(int i = 0;i < n;++i) { scanf("%d",&a); a = __gcd(a,m); if(a == 1) flag = 1; for(int j = 0;j < g.size();++j) if(g[j] % a == 0) num[j] = 1; } printf("Case #%d: ",ca++); if(flag) { printf("%lld\n",(ll)m*(m-1)/2); continue; } ll ans = 0; for(int i = 0;i < g.size();++i) { if(num[i] == 0) continue; ans += (ll)(m/g[i] - 1) * m/2*num[i]; for(int j = i + 1;j < g.size();++j) { if(g[j] % g[i] == 0) num[j] -= num[i]; } } printf("%lld\n",ans); } return 0; }
第二种做法就是欧拉函数,我觉得很巧妙。
还是和上面一样,每次都是经过k*gcd(ai,m)的格子,对于重复计算的我们规定,第i块石头只能由gcd = gcd(i,m) 的青蛙经过.(如果不存在这样的青蛙我们假设添加一个这样的青蛙,对结果的求和是没有影响的).
对于样例可以得出如下结论:
2 12
9 10
步数为2 的经过 2 10
步数为3 的经过 3 9
步数为4的经过 4 8
步数为6的经过 6 . 综上就消除了所有重复的情况.
他们每个对答案的贡献:
2*(1 +5),3*(1+3),4*(1+2),6*(1) 可以发现一个神奇的东西,对于每个x,他的贡献为 x*(与mx互质的所有数的和).
存在一个结论:
[1,x]中与x互质的所有的数和为 φ(x)∗x2 ,知道这个结论这个题目就简单了.
预处理m的所有因子,然后记录所有gcd(ai,m)。然后枚举每个m的因子,先判断该石头能否被走过,(即是否有gcd可以整除该因子).如果被走过就按照公式暴力计算就好了.
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+5; int a,g[maxn]; int fac[maxn],len; int n,m; void init() { len = 0; int tmp = m; for(int i = 2;i*i <= m;++i) { if(tmp % i == 0) { fac[len++] = i; if(i != tmp / i) fac[len++] = tmp / i; } } return ; } int phi(int x) { int res=x,a=x; for(int i=2;i*i<=x;i++) { if(a%i==0) { res-=res/i; while(a%i==0) a/=i; } } if(a>1) res-=res/a; return res; } int main() { int _; cin>>_; int ca = 1; while(_--) { scanf("%d %d",&n,&m); init(); bool flag = 0; for(int i = 0;i < n;++i) { scanf("%d",&a); if(a > m) a %= m; g[i] = __gcd(a,m); if(g[i] == 1) flag = 1; } printf("Case #%d: ",ca++); if(flag == 1) { printf("%lld\n",(ll)m*(m-1)/2); continue; } sort(g,g+n); n = unique(g,g+n) - g; sort(fac,fac+len); ll ans = 0; for(int i = 0;i < len;++i) { for(int j = 0;j < n ;++j) { if(fac[i] % g[j]) continue; ans += (ll)m*phi(m/fac[i])/2; break; } } printf("%lld\n",ans); } return 0; }
相关文章推荐
- HDU 5514 Frogs 欧拉函数
- hdu 5514 Frogs 容斥或欧拉函数
- hdu 5514 Frogs 欧拉函数
- HDU 5514 Frogs 容斥定理
- HDU - 5514 - Frogs 【完美使用欧拉函数 -> 也可容斥】
- HDU 5514 Frogs(欧拉函数+数论YY)
- HDU 5514 Frogs 容斥原理
- HDU 5514 Frogs(容斥)
- HDU 5514 (Frogs) (容斥原理)
- hdu 5514 Frogs 2015沈阳区域赛 数论 欧拉 好题 开心的题
- 2018-01-15 HDU 5514 Ver.A 数论 GCD 欧拉函数 总结归纳
- HDU 5514 Frogs(容斥问题)
- HDU-5514 Frogs
- HDU 5514 Frogs
- HDU 5514 欧拉函数应用
- HDU 5514 Frogs
- HDU 5514 Frogs(巧妙地容斥)
- HDU 5514 (ACM 2015 沈阳) Frogs [容斥+记忆化搜索]
- HDU 5514 Frogs(容斥原理)——2015ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学)
- hdu 5514 (欧拉函数的应用)