HDU - 5514 Frogs (2015 ICPC沈阳 容斥)
2017-09-28 22:30
381 查看
题目描述:
点击打开链接
题意,有m个石头,标号从0-m-1,有n只青蛙,所有青蛙一开始都在编号为0的石头上,每只青蛙每次可以跳的距离为a[i],青蛙每跳到一个石头上就会占领这个石头,求所有青蛙能够占领的石头的编号之和。
首先考虑对于每个青蛙他能够占领的所有石头都是gcd(a[i],m)的倍数,那么对于m以内gcd(a[i],m)的所有倍数实际上是一个等差数列,我们可以通过等差数列求和求的对于一个青蛙,设gcd(a[i],m)=x,它所经过的所有石头的编号和为(x+x*(m/x-1))*(m/x-1)/2=x*(m/x)*(m/x-1)/2,这里的(m/x-1)需要解释一下,首先gcd(a[i],m)=x,这个x一定是m的因子的所以一定能够整除,有这题的编号是从0-m-1,所有m这个石头是不算的,所以要把这个石头减去。然后对于每只青蛙我们都可以这样算出一个和,但是里面有重复的部分,这就涉及到麻烦的去重。关于的去重的部分,我的处理时,先将m的所有因子枚举出来,然后对于要计算的因子进行标记,进行计算时需要同时统计其他因子已经计算的次数,对于多计算的需要减掉,少计算的加上。详见代码注释。
AC代码:
点击打开链接
题意,有m个石头,标号从0-m-1,有n只青蛙,所有青蛙一开始都在编号为0的石头上,每只青蛙每次可以跳的距离为a[i],青蛙每跳到一个石头上就会占领这个石头,求所有青蛙能够占领的石头的编号之和。
首先考虑对于每个青蛙他能够占领的所有石头都是gcd(a[i],m)的倍数,那么对于m以内gcd(a[i],m)的所有倍数实际上是一个等差数列,我们可以通过等差数列求和求的对于一个青蛙,设gcd(a[i],m)=x,它所经过的所有石头的编号和为(x+x*(m/x-1))*(m/x-1)/2=x*(m/x)*(m/x-1)/2,这里的(m/x-1)需要解释一下,首先gcd(a[i],m)=x,这个x一定是m的因子的所以一定能够整除,有这题的编号是从0-m-1,所有m这个石头是不算的,所以要把这个石头减去。然后对于每只青蛙我们都可以这样算出一个和,但是里面有重复的部分,这就涉及到麻烦的去重。关于的去重的部分,我的处理时,先将m的所有因子枚举出来,然后对于要计算的因子进行标记,进行计算时需要同时统计其他因子已经计算的次数,对于多计算的需要减掉,少计算的加上。详见代码注释。
AC代码:
#include<iostream> #include<sstream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<set> #include<queue> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; const int MAXM=100010; long long n,m,cnt; long long a[MAXM]; long long vis[MAXM]; long long num[MAXM]; /*void init() { memset(prime,true,sizeof(prime)); prime[2]=true; cnt=0; for (int i=2;i<MAXM;i++) { if (prime[i]) { rec[cnt++]=i; for (int j=i*2;j<MAXM;j=j+i) prime[j]=false; } } }*/ long long gcd(long long x,long long y) { return y==0? x:gcd(y,x%y); } int main() { int T; scanf("%d",&T); int cas=1; while(T--) { scanf("%lld%lld",&n,&m); int cnt=0; for (long long i=1;i*i<=m;i++) { if (m%i==0) { a[cnt++]=i; if (i*i!=m) a[cnt++]=m/i; } } sort(a,a+cnt); long long x; memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num));//num数组用来计算每个因子已经被计算了多少次。 for (int i=1;i<=n;i++) { scanf("%d",&x); x=gcd(x,m); for (int j=0;j<cnt-1;j++) if (a[j]%x==0) vis[j]=1;//这个标记表示这个应该被计算几次,所有 4000 出现过的因子都应该被计算一次,没有出现过的不应该被计算。 } long long ans=0; for (int i=0;i<cnt-1;i++) { if (num[i]!=vis[i]) {//如果已经被计算的次数与应该被计算的次数不等说明我们需要继续进行计算。 ans+=((m/a[i])*(m/a[i]-1)/2)*a[i]*(vis[i]-num[i]);//vis[i]-num[i]表示如果之前被多计算了需要减去,少计算了需要被加上。 long long tmp=vis[i]-num[i]; for (int j=i;j<cnt-1;j++) {//对于其他因子的标记,这个应该好理解,比如我在计算2的倍数的时候,实际上4,6,8等2的倍数都已经被计算过一次了,那么我们就需要记录这些倍数的因子被多算或少算的次数。 if (a[j]%a[i]==0) num[j]+=tmp; } } } printf("Case #%d: %lld\n",cas,ans); cas++; } return 0; }
相关文章推荐
- HDU 5514 【2015沈阳现场赛 F】 Frogs
- HDU 5514 Frogs (2015沈阳F题&&容斥+剪枝)
- HDU 5514 Frogs(容斥原理)——2015ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学)
- HDU 5514 (ACM 2015 沈阳) Frogs [容斥+记忆化搜索]
- hdu 5514 Frogs 2015沈阳区域赛 数论 欧拉 好题 开心的题
- HDU - 5514 Frogs (容斥)
- HDU 5521 2015ACM-ICPC沈阳赛区现场赛M题
- HDU - 5514 - Frogs 【完美使用欧拉函数 -> 也可容斥】
- HDU 5514 Frogs(容斥)
- Frogs (hdu5514)——2015ACM/ICPC亚洲区沈阳站(容斥定理)
- 2015ACM/ICPC亚洲区沈阳站-重现赛 HUD 5514 Frogs (容斥原理+GCD)
- HDU 5510 2015ACM-ICPC沈阳赛区现场赛B题
- 2015沈阳现场赛F (HDU 5514)(经典问题 数论phi函数)
- HDU Frogs 5514 容斥
- HDU-5512(Pagodas) 2015 ACM/ICPC 亚洲区沈阳赛区 (题目编号1004)
- HDU 5512 2015ACM-ICPC沈阳赛区现场赛D题
- HDU 5514 Frogs(容斥)
- HDU 5514 Frogs(巧妙地容斥)
- HDU 5514 Frogs (数论容斥)
- HDU 5514 Frogs(容斥问题)