HDU5514 变形巧妙运用容斥定理

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

All frogs start their jump at stone 00, 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 2020), and the first line contains an integer tt, 
meaning the total number of test cases. 

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

The second line contains nn integers a1,a2,⋯,ana1,a2,⋯,an, where aiai denotes step length of the ii-th frog (1≤ai≤109)(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

 

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define init(a,b) memset(a,b,sizeof a)
const int MAXN = 1e4+10;
int a[MAXN];
int d[MAXN];
int vis[MAXN];
int num[MAXN];
//要求所有frogs跳过的石头的序号,那么跳过的石头一定都是gcd(a[i],m)的倍数,最大就能跳到m-1的石头
//为什么,你可以模拟一下,大于m的时候取一下模就得出,比如第一组样例 12 9 那么跳过的石头的序号排列
//之后就是 3 6 9 12(0), 但是gcd(9,12)=3与gcd(10,12)=3的倍数中有重复的数,那就需要去重,怎么去,看一下
//所有被踩过的石头的序号构成一个等差数列,个数为m/gcd(a[i],m),公差为gcd(a[i],m);这样就能求出每一个因子
//所到达的序列总和,然后剩下的就是去重,下面要引入一个名称,其他博客里面写的叫做贡献次数,那我就也叫贡献次数
//下面就结合代码解释
int main()
{
    int t;
    int n, m;
    freopen("in.txt","r",stdin);
    scanf("%d", &t);
    for(int cas = 1; cas <= t; cas++) {
        init(vis,0);init(num,0);
        int cnt=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i*i<=m;i++)//先将所有的m里面的因子找到,为下面说明哪些因子用到了
        {
            if(m%i==0){
                d[cnt++]=i;
            if(i*i!=m)
                d[cnt++]=m/i;
            }
        }
        sort(d,d+cnt);//sort一下便于下面的计算,这样2的倍数6的倍数好处理
        rep(i,0,n-1)
        {
            scanf("%d",&a[i]);
            a[i]=__gcd(a[i],m);
            rep(j,0,cnt-1){
            if(d[j]%a[i]==0) vis[j]=1;//说明b[j]里面的因子是gcd的倍数,那么就要看下面的要不要去重了,
            //这是为下面num数组结合所用的
            }
        }
        LL ans=0;
        //num数组存放的是第i个因子(指再d数组里面存放的)的贡献次数值,开始初始化为0,然后从最小的
        //因子开始求所能到达的石头的序列的和,如果没有该因子,那都是0,就不会进入下面求和的步骤,或者
        //当所有的a[i]与m的gcd没有相同的因子时也不会有求和的步骤,比如第一组数据3与2中,2的倍数4就不会再次求和
        //步骤,但时当两者有相同的公倍数那就需要去重了
        rep(i,0,cnt-1)
        {
            if(vis[i]!=num[i]){
                LL t=m/d[i];//当因子为d[i]时,能够跳的石头的个数
                ans+=t*(t-1)/2*d[i]*(vis[i]-num[i]);//后面的vis[i]-num[i]是看两者是否有多次贡献
//在第一组样例中6就被用了两次求和,那就需要减去,减去就是加上负数vis[i]-num[i];
//完成去重,因此只改变num[i]的数就能知道因子d[i]被重复用了多少次,下面是对num修改
                for(int j=i+1;j<cnt;j++)
                    if(d[j]%d[i]==0)//num最大为2
                        num[j] +=vis[i]-num[i];
            }
        }
        printf("Case #%d: %I64d\n", cas, ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值