【2015沈阳区域赛F=HDU5514】Frogs(圆上n个青蛙跳统计跳劲哪些点---欧拉函数求和+思维)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5514

题目:


Problem Description

There are m stones lying on a circle, and n frogs are jumping over them.
The stones are numbered from 0 to m−1 and the frogs are numbered from 1 to n. The i-th frog can jump over exactly ai stones in a single step, which means from stone j mod m to stone (j+ai) mod m (since all stones lie on a circle).

All frogs start their jump at stone 0, then each of them can jump as many steps as he wants. A frog will occupy a stone when he reach it, and he will keep jumping to occupy as much stones as possible. A stone is still considered ``occupied" after a frog jumped away.
They would like to know which stones can be occupied by at least one of them. Since there may be too many stones, the frogs only want to know the sum of those stones' identifiers.

Input

There are multiple test cases (no more than 20), and the first line contains an integer t,
meaning the total number of test cases.

For each test case, the first line contains two positive integer n and m - the number of frogs and stones respectively (1≤n≤104, 1≤m≤109).

The second line contains n integers a1,a2,⋯,an, where ai denotes step length of the i-th frog (1≤ai≤109).

Output

For each test case, you should print first the identifier of the test case and then the sum of all occupied stones' identifiers.

Sample Input

3
2 12

9 10

3 60

22 33 66

9 96

81 40 48 32 64 16 96 42 72

Sample Output

Case #1: 42

Case #2: 1170

Case #3: 1872

解题思路:


gcd(a[i],m)是青蛙i从0开始每次跳的步长(此处的步长是指将青蛙最终经过的所有石头按照从小到大的编号排列,相邻两石头之间的间隔),当存在gcd(a[i],m)=1是,一定所有的石头都经过了,此处可特判。

石头x,如满足gcd(a[i],m)|x,那么从0开始以gcd(a[i],m)为步长一定能经过x,但是若有多个i满足gcd(a[i],m)|x,x要避免重复统计,只能让唯一的一个步长经过x时才统计x,所有我们令只有步长为gcd(x,m)是经过x才将x统计如答案。

且如果存在步长gcd(a[k],m),但不存在步长gcd(x,m),只要满足gcd(a[k],m)|gcd(x,m),那么以gcd(a[k],m)为步长经过x是将x统计入答案。

gcd(x,m)一定为大于1的因子(因为所有石子都经过的情况已经特判了),所有只需要枚举m的因子即可,且m比较大,要O(sqrt(n))枚举,若i是因子,那么m/i必定也是因子。

必备知识点:若[1.n]与n互质的数的个数为\varphi (n),那么这\varphi (n)个数之和为sum(n)=\frac{\varphi (n)*n}{2}

例如m=16

x 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
gcd(x,16) 0 1 2 1 4 1 2 1 8 1 2 1 4 1 2 1

步长2经过的点有:2、6、10、14即2*(1、3、5、7), ans+=2*sum(16/2)

步长4经过的点有:4、12即4*(1、3), ans+=4*sum(16/4)

步长8经过的点有:8即8*(1), ans+=8*sum(16/8)

ac代码:


#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+10;
typedef long long ll;
ll t, n, m, x;
ll com[maxn];
vector<ll> fac;
ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a%b);
}
ll euler(ll n)
{
    ll ans = n;
    for (ll i = 2; i * i <= n; i++)
    {
        if (n % i == 0)
        {
            ans = ans / i * (i - 1);
            while (n % i == 0) n /= i;
        }
    }
    if (n != 1) ans = ans / n * (n - 1);
    return ans;
}
bool check(ll x)
{
    for(int i = 0; i < n; i++)
        if(x%com[i] == 0)
            return true;//可到达x点
    return false;
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    scanf("%lld", &t);
    int Case = 0;
    while(t--)
    {
        fac.clear();
        ll ans = 0;
        bool Prime = false;
        scanf("%lld %lld", &n, &m);
        for(int i = 0; i < n; i++)
        {
            scanf("%lld", &x);
            com[i] = gcd(x, m);
            if(com[i] == 1) Prime = true;
        }
        if(Prime) ans = m*(m-1)/2;
        else
        {
            //获得m的所有因子(不包含1,前面已经特判),O(sqrt(m),同时进行判断
            for(int i = 2; i*i <= m; i++)
            {
                if(m%i == 0)
                {
                    if(check(i)) ans += euler(m/i)*m/2;
                    if(i*i!=m && check(m/i)) ans+= euler(i)*m/2;
                }
            }
        }
        printf("Case #%d: %lld\n", ++Case, ans);
    }
    return 0;
}

 

发布了299 篇原创文章 · 获赞 81 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/Cassie_zkq/article/details/101560583