HDU

来源:互联网 发布:电脑照片打印排版软件 编辑:程序博客网 时间:2024/06/17 07:48


题意:

有 n 个青蛙,第 i 个青蛙每次只能够跳 ai 步,现在有 m 个石头围成一圈,编号为 0 到 m−1,现在青蛙可以围着这个石头组成的圆跳无限次,每跳一次就会占领这个石头,可以无限占领,现在问你的是这 n 个青蛙占领的石头的编号的总和是多少。

思路: 
先说第一种方法: 
我们可以发现对于每个ai,他所能经过的石头为 k*gcd(m,ai).但是我们发现比如第一个样例 
2 12 
9 10 ,gcd分别为2 ,3。 
2 经过 0 2 4 6 8 10 
3经过 0 3 6 9 
这就会有重复的,比如6就会被算两次.这就需要去掉了,这时候就可以想到这个题需要容斥一下了.那么关键是怎么容斥?


现在我们已知 gi=gcd(m,ai), 我们还是考虑 m 的因子x,如果这个因子 x 符合条件,那么我们来计算 x 对答案做出的贡献: (mx1)m2。 
首先我们预处理出 m 的除了 1  m 的因子 fac[i],如果这个因子fac[i]%g[i]=0 的话(其中 g[i]为可能出现的最大公约数),那么我们将其用一个 vis 数组进行标记为 1,然后再用一个数组 num 记录当前因子对答案做过的贡献,初始值为 0,那么有第i 个因子做出的贡献为: (mx1)m2(vis[i]num[i]) 
因为可能有重复的,所以我们就对所有能够整除因子 fac[i] 的因子全都加上 vis[i]num[i], 然后就可以计算答案了。


总结:很好的容斥把, 对于一个gcd, 如果把他加入贡献,他所有的倍数也都加入了贡献, 所以我们把所有a[i]跟m的gcd,在m因子里的倍数都标记一下vis, 然后枚举m的因子, 用num记录每个数贡献了几次, 当前枚举了一个数, 他所有的倍数都贡献了一次,下次他倍数再出现就不用算贡献了,每个数只能贡献一次, 所以其他枚举到的数,如果他的倍数有之前贡献过一次的数了,他就要减去了。很好的容斥。还是要观察题目, gcd(m, a),这不就是说所有gcd都是m的因子,他的lcm也是m的因子吗。。


#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <vector>#include <algorithm>using namespace std;typedef long long ll;const int maxn = 1e4 + 5;int cnt[maxn], n, m;vector<int> fac;void init(){    fac.clear();    memset(cnt, 0, sizeof(cnt));    for(int i = 1; i*i <= m; i++)    {        if(m % i == 0)        {            fac.push_back(i);            if(i != m/i) fac.push_back(m/i);        }    }}int main(){    int _, ca = 1;    cin >> _;    while(_--)    {        scanf("%d%d", &n, &m);        init();        sort(fac.begin(), fac.end());        int x, flag = 0;        for(int i = 0; i < n; i++)        {            scanf("%d", &x);            int gcd = __gcd(m, x);            if(gcd == 1) flag = 1;            for(int j = 0; j < fac.size(); j++) //记录所有有贡献 的因子                if(fac[j]%gcd == 0)                    cnt[j] = 1;        }        if(flag)        {            printf("Case #%d: %lld\n", ca++, (ll)m*(m-1)/2);            continue;        }        ll ans = 0;        for(int i = 0; i < fac.size(); i++)        {            if(!cnt[i])  continue;            ans += (ll)(m/fac[i]-1)*m/2*cnt[i];            for(int j = i + 1; j < fac.size(); j++)                if(fac[j]%fac[i] == 0)                    cnt[j] -= cnt[i];        }        printf("Case #%d: %lld\n", ca++, ans);    }    return 0;}



原创粉丝点击