HDU-5514 Frogs 容斥

HDU-5514 Frogs

题意

(m)个石头连成环,从(0sim m-1)标号,有(n)只青蛙初始时都在(0)号石头上,第(i)只青蛙一次能跳(a_i)步,问有多少石头能被至少一个青蛙到达。

分析

对于第(i)只青蛙,他能跳到所有标号为(gcd(a_i,m))的倍数的石头,所以我们只需要考虑(m)的因子对答案的贡献。将(m)中所有能到达的因子的系数(vis[i])设为1其余为0,进行容斥,从小到大枚举每个因子,计算当前因子(x)的贡献即为(vis[i]cdot frac{(frac{m}{x}-1) cdot m}{2})用到了这个因子之后要将后面的是它的倍数的因子的系数减去这个因子的系数,理解为因子(x)的倍数被计算了(vis[i])遍,所以后面是(x)的倍数的因子的倍数就要少计算(vis[i])遍。

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<=n;++i)
#define per(i,n,a) for (int i=n;i>=a;--i)
#define sz(x) ((int)(x).size())
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
typedef pair<int,int> pii;
#define ll long long
const int inf=1e9;
const int mod=1e9+7;
const int N=1e4+10;
int T,n,m;
int a[N],vis[N];
ll cal(int x){
	ll p=m/x;
	return p*(p-1)*x/2;
}
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}
int main(){
	//ios::sync_with_stdio(false);
	//freopen("in","r",stdin);
	scanf("%d",&T);
	for(int cas=1;cas<=T;cas++){
		scanf("%d%d",&n,&m);
		memset(vis,0,sizeof(vis));
		vector<int>q;
		q.pb(1);
		for(int i=2;i*i<=m;i++) if(m%i==0){
			q.pb(i);
			if(i!=m/i) q.pb(m/i);
		}
		sort(q.begin(), q.end());
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			a[i]=gcd(a[i],m);
		}
		sort(a+1,a+n+1);
		n=unique(a+1,a+n+1)-a-1;
		for(int i=1;i<=n;i++){
			for(int j=0;j<sz(q);j++){
				if(q[j]%a[i]==0) vis[j]=1;
			}
		}
		ll ans=0;
		for(int i=0;i<sz(q);i++) if(vis[i]!=0){
			ans+=cal(q[i])*vis[i];
			for(int j=i+1;j<sz(q);j++) if(q[j]%q[i]==0) vis[j]-=vis[i];
		}
		printf("Case #%d: %lld
", cas, ans);
	}
	return 0;
}