题意 输入n,m,n代表青蛙个数 m代表石头个数 石头围成一圈 由0编号到m-1 第二行输入n只青蛙的步长,一块石头不能被两只青蛙同时占领,求石头编号的和,以第一组样例
2 12
9 10 为例
2 即两只青蛙 12是石头个数 两只青蛙最开始都在编号为0的石头上 第一只青蛙 可以跳到
编号9
(9+9)%12 编号6
(18+9)%12 编号3
(27+9)%12 编号0
跳回原处 所以第一只青蛙是 0 3 6 9
第二只青蛙 同上述过程是 0 2 4 6 8 10
出现了相同的6 这时候就需要容斥来把 重复的去掉
易得青蛙的步长为gcd(a[i],m)
因为是gcd(a[i],m)所以一定是m的因子 把m的因子先预处理出来
然后x(m/x*(m/x-1))/2求出来每个因子能被占领的标号的和
通过两个数组一个标记是否访问一个标记计算次数 【大佬的巧妙容斥
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int t[maxn];
int num[maxn],v[maxn]; //被计算的次数 是否被访问
int main()
{
int T;
cin>>T;
int n, m;
int d=1;
while(T--)
{
cin>>n>>m;
memset(num,0,sizeof(num));
memset(v,0,sizeof(v));
int cnt=0;
for(int i=1;i*i<=m;i++) //m的因子
{
if(m%i==0)
{
t[cnt++]=i;
if(i*i<m)
t[cnt++]=m/i;
}
}
sort(t,t+cnt);
int tt,h;
for(int i=0;i<n;i++)
{
cin>>tt;
tt=gcd(tt,m);
for(int j=0;j<cnt;j++) //这个因子都可以被跳到 //去掉m
if(t[j]%tt==0)
v[j]=1;
}
v[cnt-1]=0;
ll ans=0;
for(int i=0;i<cnt;i++)
{
if(v[i]!=num[i]) //学习大佬的容斥
{
int f=(m-1)/t[i];
ans+=((ll)f*(f+1)/2*t[i]*(v[i]-num[i]));
int c=v[i]-num[i];
for(int j=i;j<cnt;j++) //修改当前因子的倍数的的计算次数
if(t[j]%t[i]==0)
num[j]+=c;
}
}
printf("Case #%d: %lld\n",d++,ans);
}
return 0;
}
还有一种方法是欧拉函数的做法也是超级巧妙
可以看看这个博客->点击打开链接
AC代码
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
long long ol(long long a)
{
long long res,i,t=0;
res=a;
for(i=2;i*i<=a;i++)
{
if(a%i==0)
{
res=res-res/i;
while(a%i==0)
{
a=a/i;
}
}
}
if(a>1)
{
res=res-res/a;
}
return res;
}
int t[maxn];
int v[maxn],num[maxn]; //被计算的次数 是否被访问
int main()
{
int T;
cin>>T;
int n, m;
int d=1;
while(T--)
{
cin>>n>>m;
int cnt=0;
for(int i=1;i*i<=m;i++) //m的因子
{
if(m%i==0)
{
t[cnt++]=i;
if(i*i<m)
t[cnt++]=m/i;
}
}
sort(t,t+cnt);
memset(v,0,sizeof(v));
int tt,h;
int f=0;
for(int i=0;i<n;i++)
{
cin>>tt;
tt=gcd(tt,m);
for(int j=0;j<cnt;j++) //这个因子都可以被跳到 //去掉m
if(t[j]%tt==0)
v[j]=1;
}
v[cnt-1]=0;
ll ans=0;
for(int i=0;i<cnt;i++)
{
if(v[i]==1)
{
int f=m/t[i];
ans+=t[i]*ol(f)*f/2;
}
}
printf("Case #%d: %lld\n",d++,ans);
}
return 0;
}