HDU 5514 Frogs (容斥思想+数论知识)*

Frogs

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 4142    Accepted Submission(s): 1396


 

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

 

Source

2015ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学)

 

Recommend

wange2014   |   We have carefully selected several similar problems for you:  6437 6436 6435 6434 6433 

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
/*
题目大意:n个青蛙,每个青蛙在一个0~m-1的环上,
从0出发,每个都有步数,要求开所有能被青蛙踩到的环下标和。

可以知道x=gcd(x,m)(模拟一下就可以感受到)
一开始用最普通的容斥做,dfs容斥,
就是只保留最小的因子(不一定是质数),
然后利用这些来进行容斥,光荣超时/。。。。。

最后还是采用了贡献的思想,
一旦有个因子能被青蛙走到,其倍数的贡献都加一,

在后面遍历计算时,如果该枚举的因子贡献不为零,则计算它的贡献值(要乘上其vis值,可能为负),
然后对于它的倍数减去它的贡献,
这样配合筛法进行容斥(我感觉筛法就是数论中的Dp精髓)。

可以看到vis数组的值变化在-1,0和1区间内。
并且该算法对每个因子维护的vis数组其实就是它的重复度,
本质上,比如有个数x,被前面的因子筛了s次,那么判别到它时肯定要减去它的重复度,
因为被筛了肯定也被初始化为1,所以重复度为s-1,减去后,后面的倍数也被更新,
被更新为0,因为前面因子的出现导致后面的倍数无影响或影响被覆盖,符合题目精髓。。。

*/

const int  maxn =2e4+5;
const int mod=1e9+7;
ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}

int n,m,x;
int yinzi[maxn],tot=0;
int vis[maxn];
///容斥的过程

int main()
{
    int t;scanf("%d",&t);
    for(int ca=1;ca<=t;ca++)
    {
        scanf("%d%d",&n,&m);

        tot=0;yinzi[tot++]=1;///注意筛选因子的时候要加上1
        for(int i=2;i*i<=m;i++)
        {
            if(m%i==0)
            {
                if(i*i==m) yinzi[tot++]=i;
                else
                {
                    yinzi[tot++]=m/i;
                    yinzi[tot++]=i;
                }
            }
        }

        sort(yinzi,yinzi+tot);
        memset(vis,0,sizeof(vis));

        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            x=gcd(x,m);///青蛙的步伐
            for(int j=0;j<tot;j++)   if(yinzi[j]%x==0) vis[j]=1;///初始化所有步数的倍数都要有贡献
        }

        ll ans=0;
        for(int i=0;i<tot;i++)
        {
            if(vis[i])
            {
                ll tp=m/yinzi[i];
                ans+=1LL*yinzi[i]*(tp-1)*tp/2*vis[i];
                for(int j=i+1;j<tot;j++)
                {
                    if(yinzi[j]%yinzi[i]==0)
                        vis[j]-=vis[i];///计算过的就不用计算了,减去它的贡献,另一类容斥
                }
            }
        }

        printf("Case #%d: %lld\n",ca,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/82047748