2015 ICPC沈阳现场赛 F. Frogs (欧拉函数)

题目链接

m个石头围成一圈,一群青蛙从0开始跳,第i个青蛙每步跳ai距离,求所有能被跳到的石头的编号之和。

容易推出石头x被第i只青蛙跳到的充要条件是gcd\left ( a_{i},m \right )| x,显然这与下面的命题是等价的:

石头x被跳到的充要条件是存在一个i,使得gcd\left ( a_{i},m \right )| gcd\left ( x,m \right )

gcd(x, m)显然是m的因数,那么我们就可以枚举m的因数。对于m的一个因数k,如果存在一个i使得k能被ai整除,则此时它产生的贡献就是:

\sum_{gcd(x,m)==k}^{ }x=k*\sum_{gcd(x/k,m/k)==1}^{ }x/k

实际上上面也就是小于m/k且与m/k互质的数之和,有这样一个结论:小于x且与x互质的数之和为\frac{x\varphi (x)}{2}。上式即为:

k*\frac{\varphi (m/k)*m/k}{2}=\frac{\varphi (m/k)*m}{2}

枚举m的因数计算即可。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e4+5;

int t, n, no;
ll m, x, num[maxn];

ll eul(ll x)
{
    ll ans = x;
    for(ll i = 2;i*i <= x;i++)
    {
        if(x % i == 0)
        {
            ans -= ans/i;
            while(x % i == 0)
                x /= i;
        }
    }
    if(x > 1) ans -= ans/x;
    return ans;
}

bool check(ll x)
{
    for(int i = 0;i < no;i++)
    {
        if(x % num[i] == 0)
            return true;
    }
    return false;
}

ll gcd(ll a, ll b)
{
    if(b == 0) return a;
    return gcd(b, a % b);
}

int main()
{
    scanf("%d", &t);
    int kase = 0;
    while(t--)
    {
        scanf("%d%lld", &n, &m);
        no = 0;
        for(int i = 1;i <= n;i++)
        {
            scanf("%lld", &x);
            num[no++] = gcd(x, m);
        }
        sort(num, num + no);
        no = unique(num, num + no) - num;
        ll ans = 0;
        for(ll i = 1;i*i <= m;i++)
        {
            if(m % i != 0) continue;
            if(check(i)) ans += eul(m/i);
            if(i == 1 || i*i == m) continue;
            if(check(m/i)) ans += eul(i);
        }
        ans = ans*m/2;
        printf("Case #%d: %lld\n", ++kase, ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/82833542