题目描述:有n只青蛙,m个石头(围成圆圈)。第i只青蛙每次只能条a[i]个石头,问最后所有青蛙跳过的石头的下标总和是多少?
思路:经过绘图我们发现,每次跳过的位置一定是k*gcd(a[i], m)。然后我就不会了= =。于是看了别人的题解,方法挺好的。
找出所有的m的因子,然后对能通过gcd(a[i],m)得到的因子用vis数组标记,然后用num[i]表示该因子重复计算了几次,然后利用重复计算的次数来容斥一下就出来了。具体看代码吧
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second #define haha printf("haha ") const int maxn = 1e4 + 5; LL a[maxn]; int kase, n; LL m; int vis[maxn], num[maxn]; LL gcd(LL a, LL b){ return b == 0 ? a : gcd(b, a % b); } int main(){ int t; cin >> t; while (t--){ scanf("%d%I64d", &n, &m); vector<LL> v; for (LL i = 1; i <= sqrt(m) + 1; i++){ if (m % i == 0){ v.push_back(i); if (i * i != m) v.push_back(m / i); } } memset(vis, 0, sizeof(vis)); memset(num, 0, sizeof(num)); sort(v.begin(), v.end()); for (int i = 1; i <= n; i++){ scanf("%I64d", a + i); LL g = gcd(a[i], m); for (int j = 0; j < v.size(); j++){ if (v[j] % g == 0) vis[j] = 1; } } LL ans = 0; for (int i = 0; i < v.size(); i++){ if (vis[i] != num[i]){ LL val = v[i]; LL tmp = m / val; ans += (val * tmp) * (tmp - 1) / 2 * (vis[i] - num[i]); tmp = vis[i] - num[i]; ///printf("tmp = %I64d val = %I64d ", tmp, val); for (int j = i; j < v.size(); j++){ if (v[j] % val == 0) num[j] += tmp; } } } printf("Case #%d: %I64d ", ++kase, ans); } return 0; }