题意: m 个石头标记 0~m-1,然后 n 个青蛙开始都在石头 0 上,每个青蛙每次跳 x 块石头,求最后能被青蛙跳上去的石头的值的和。
首先注意到每只青蛙每次跳的石头号为 gcd(x, m),然后把 m 的所有因子(最多 log2(m)个)拿出来,设 temp 为每个 gcd,将因子中每个 temp 的倍数都标记为 1,然后再用 num 数组代表某个因数被跳跃的次数。
注意一下由于 0~m-1,所以 m 这个数不会被跳上去(其实是 0)。
#include<cstring> #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<string> #include<iostream> #include<algorithm> using namespace std; long long p[10005]; long long num[10005];//被加了几次 long long vis[10005]; int main(){ //freopen("a.txt", "r", stdin); int t; cin >> t; for(int cas = 1; cas <= t; cas++){ long long ans = 0; memset(vis, 0, sizeof(vis)); memset(num, 0, sizeof(num)); long long m, n; scanf("%lld %lld", &n, &m); int cnt = 0; //取出m的所有因子 for(int i = 1; i <= sqrt(m); i++){ if(m%i == 0){ p[cnt++] = i; if(i*i != m) p[cnt++] = m/i; } } sort(p, p+cnt); //对于每个青蛙跳跃的步数分别求出gcd,然后在因子中标记gcd的倍数为1 for(int i = 0; i < n; i++){ long long q; scanf("%lld", &q); int tem = __gcd(q, m); for(int j = 0; j < cnt; j++){ if(p[j]%tem == 0) vis[j] = 1; } } vis[cnt-1] = 0; //由于0~m-1,所以没有m这个数 for(int i = 0; i < cnt; i++){ if(vis[i] != num[i]){ ans += m*(p[i]+m)/p[i]/2*(vis[i]-num[i]); //若p[i]=2,则求2+4+6+…+m的和 for(int j = i+1; j < cnt; j++) if(p[j]%p[i] == 0) num[j] += (vis[i]-num[i]); //更新未计算的num的值 } } printf("Case #%d: %lld\n", cas, ans); } return 0; }