hdu5514Frogs+容斥

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
题意:有m个石子围成一圈, 有n只青蛙从跳石子, 都从0号石子开始, 每只能越过xi个石子。问所有被至少踩过一次的石子的序号之和。
首先对于每个青蛙而言,它能到达的位置是它每次能跳的长度x与m的GCD的倍数。但是对于很多的GCD的时候。可能会多算,,比如2的倍数,3的倍数,此时6的倍数就被多算了。。
其实也就是上边的那个思想用容斥的办法,既然6多算了一次,剪掉一次6的倍数就好。。
GCD(x,m)=p,p肯定是m的约束。我们首先考虑所有m的约数,,如果是青蛙能跳的倍数的就标记为1,表示这个约数要做出贡献值。。如果约束q做出了贡献,那么q的倍数的贡献也就要标记上。。
比如计算2的时候6被标记了一次,计算3的时候,6被也标记了一次,计算到6的时候,本应爱是visit==1但是我有num==2,标记计算了两次,所以要剪掉。visit-num,num也有可能是多次反复跟新约数的贡献值的次数。。
/****************************
然后:计算某个值q的倍数的贡献值。。即0-m-1上q的倍数的累加和..显然这是个等比数列。
所以末项=(m-1)/q;公差为q,项数=(m-1)/q+1(0开始)
o-m-1内的数的累加和=(0+(m-1)q)((m-1)/q+1)/2
再乘上他的贡献次数。。

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

int fact[10004];
int visit[10004]; //需要的次数
int num[10004];//算了多少次
int main(){
    int t,n,m,x;
    cin>>t;
    for(int cas=1;cas<=t;cas++){
        cin>>n>>m;
        memset(visit,0,sizeof(visit));
        memset(num,0,sizeof(num));
        int tot=0;
        for(int i=1;i*i<=m;i++){  //找出m的约数
            if(m%i==0){
                fact[tot++]=i;
                if(i*i!=m) fact[tot++]=m/i;
            }
        }
        sort(fact,fact+tot);
        for(int i=0;i<n;i++){
            cin>>x;
            x=__gcd(x,m);//所有x的倍数是可以被访问到的。
            for(int j=0;j<tot;j++){  //所有该有GCD初始化为1
                if(fact[j]%x==0)  visit[j]=1;
            }
        }
        LL ans=0;
        visit[tot-1]=0;

        for(int i=0;i<tot;i++){
            if(visit[i]!=num[i]){
                LL CN=(m-1)/fact[i];
                ans+=CN*fact[i]*(CN+1)/2*(visit[i]-num[i]);//等差数列求和*贡献次数
                for(int j=i+1;j<tot;j++){      //更新贡献次数(约数是从小到大的)
                    if(fact[j]%fact[i]==0) num[j]+=visit[i]-num[i];
                }
            }
        }
        cout<<"Case #"<<cas<<": "<<ans<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值