gcd+容斥
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 1e4 + 10; 5 ll n,m; 6 int fac[maxn]; 7 int vis[maxn]; 8 int num[maxn]; 9 10 int main() 11 { 12 int T; 13 scanf("%d",&T); 14 for(int ii = 1; ii <= T; ii ++) 15 { 16 memset(vis,0,sizeof(vis)); 17 memset(num,0,sizeof(num)); 18 scanf("%I64d%I64d",&n,&m); 19 int tot = 0; 20 for(int i = 1; i * i <= m;i ++) 21 { 22 //找到所有m的因子 23 if(m % i == 0) 24 { 25 fac[tot ++] = i; 26 if(i * i != m) 27 fac[tot ++] = m/i; 28 } 29 } 30 //对因子排序 31 sort(fac,fac + tot); 32 tot --; 33 for(int i = 1; i <= n;i ++) 34 { 35 ll x; 36 scanf("%I64d",&x); 37 //每只青蛙能跳的石头是石头数与每只青蛙跳的距离的最大公约数的倍数,先找到d最大公约数 38 ll d = __gcd(x,m); 39 //找到所有m因子中该青蛙能跳到的,记录为1 40 for(int j = 0; j < tot; j ++) 41 if(fac[j] % d == 0) 42 vis[j] = 1; 43 } 44 ll ans = 0; 45 for(int i = 0; i < tot; i ++) 46 { 47 if(vis[i] != num[i]) 48 { 49 ll t = (m - 1)/fac[i];//此处m-1理解为虽然石头是m个,但是石头是从0开始标号的,所以减一。则t可以算出来这个因数在m-1范围内的最大倍数 50 ans += t * (t + 1) / 2 * fac[i] * (vis[i] - num[i]);//等差数列前n项和*要乘的次数 51 //对于t * (t + 1) / 2 * fac[i]这个公式,等差数列前n项和是一个变形,用的是n(a1+an)/2,an=n*a1,变形得到的 52 for(int j = i + 1; j < tot; j ++)//再一次循环,因为前一步加的时候,所有最大公约数的倍数已经加上,所以后面万一重复要减去 53 { 54 if(fac[j] % fac[i] == 0)//发现是之前最大公约数的倍数 55 num[j] += vis[i] - num[i];//次数要改变(通过这个公式算正负零,后面统一加上就是答案) 56 } 57 } 58 } 59 printf("Case #%d: %I64d ",ii,ans); 60 } 61 return 0; 62 }