很好的容斥思想 HDU 5514

题目描述:有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;
}
View Code
原文地址:https://www.cnblogs.com/heimao5027/p/5961113.html