HDU-5514 Frogs

题目描述

\(n\)只青蛙,\(m\)个围成圆圈的石头。第\(i\)只青蛙每次只能跳\(a_i\)个石头,问最后所有青蛙跳过的石头的下标总和是多少?

Input

第一行为\(T\) 表示数据组数

每一组数据第一行为\(n\)\(m\)

第二行有\(n\)个整数 表示每只青蛙一步跳的距离

\(T<=20,1<=n<=10^4,1<=m<=10^9,1<=a_i<=10^9\)

Output

对于每组数据 输出"Case #x: "(不含引号)以及答案

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(m,a_i)\)的倍数。

那么问题就变成了,给出\(n\)个数\(B_i\),问小于等于\(m\)中为\(B_i\)倍数的数的和为多少。

对于一只青蛙,其能够跳到的石头的编号是一个等差数列。

这个,我们可以利用高斯求和解决,答案就是\(((m-1)/B[i]*B[i]*((m-1)/B[i]+1))/2\)

然而,我们发现,如果把答案加上所有的青蛙的答案,其中会有很多重复的被算了好几次。

我们需要利用容斥去求解。

我们发现,会造成重复的编号一定是满足两只青蛙具有倍数关系的青蛙,并且由于是\(gcd\)所以一定是\(m\)的因子。

所以我们应该从\(m\)的因子上来考虑。

于是,我们先将\(m\)的所有因子给求出来。

然后,对于每个青蛙的步长\(A_i\)并求出\(B_i\),枚举\(m\)的因子,若该因子是青蛙\(B_i\)的倍数。

则给该因子打一个标记,由于该因子已经被计算过了一次。

所以,其倍数造成的贡献已经被它给计算过了一次,所以要减去他造成的贡献。

但由于,我们无法直接计算一个数多造成的贡献,但可以快速的计算出一个数被因子计算了几次。

既然多算了那么多次,减掉就好了。。。。

于是我们就有了这样的伪算法:

for(i:对于m的因子){
    if(该因子已经被计算的次数!=应该被计算的次数){
        int temp=该因子已经被计算的次数-应该被计算的次数;
        Ans+=temp*贡献。
        for(j:对于m的因子)if(j是当前因子i的倍数){
            //说明被多计算了。
            j已经被计算的次数+=temp;
        }
    }
}

前面我们已经说过了一个数的贡献,直接计算即可。

代码如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;

#define int long long
#define reg register
#define Raed Read
#define clr(a,b) memset(a,b,sizeof a)
#define Mod(x) (x>=mod)&&(x-=mod)
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)>(b)?(b):(a))
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])

inline int Read(void) {
	int res=0,f=1;
	char c;
	while(c=getchar(),c<48||c>57)if(c=='-')f=0;
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>=48&&c<=57);
	return f?res:-res;
}

template<class T>inline bool Min(T &a, T const&b) {
	return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a, T const&b) {
	return a<b?a=b,1:0;
}

const int N=1e4+5,M=1e5+5,mod=110119;

bool MOP1;

int A[N],B[N],vis[N],Num[N];

int gcd(int x,int y) {
	return !y?x:gcd(y,x%y);
}

bool MOP2;

inline void _main(void) {
	int T=Read(),Case=0;
	while(T--) {
		int n=Read(),m=Read(),tot=0,Ans=0;
		clr(vis,0),clr(Num,0);
		for(reg int i=1; i*i<=m; i++)if(!(m%i)) {
				B[++tot]=i;
				if(i*i!=m)B[++tot]=m/i;
			}
		sort(B+1,B+tot+1),tot--;
		rep(i,1,n) {
			A[i]=Read(),A[i]=gcd(A[i],m);
			rep(j,1,tot)if(!(B[j]%A[i]))vis[j]=1;
		}
		rep(i,1,tot) {
			if(vis[i]==Num[i])continue;
			int Now=(m-1)/B[i],temp=vis[i]-Num[i];
			Ans+=(Now*B[i]*(Now+1))*temp/2;
			rep(j,1,tot)if(!(B[j]%B[i]))Num[j]+=temp;
		}
		printf("Case #%lld: %lld\n",++Case,Ans);
	}
}

signed main() {
	_main();
	return 0;
}
posted @ 2019-09-04 08:08  dsjkafdsaf  阅读(142)  评论(0编辑  收藏  举报