题意:
有n只青蛙m块石头,每只青蛙跳的长度是固定的,问所有被跳过的石头的下标和
思路:
转化为求0~m-1之间,gcd(ai,m)的倍数的和
求一个数的倍数和是可以直接用等差数列计算出的,但是会有数被重复计算,我们要做的就是用容斥计算重复的数
首先,gcd(ai,m)一定是m的因子,所以需要容斥的数只有可能是m的因子,1e9的数的因子最多有1600+,所以可以通过预处理出m的因子来容斥
用cnt来记录容斥时每个数的计算次数,遇到多算的数就减掉,少算的数就加上,然后遍历其后的因子做相应的操作
这样有一个问题,重复算的数不一定是m的因子,比如m=36,gcd分别为3和4,24是会被重复计算的但是并不在36的因子里,这时如何保证24被计算了正确的次数?
这个问题我也想了好久,最后发现被重复计算的数和它的几个gcd的lcm的计算次数是一样的,比如说刚刚的例子,lcm(3,4)=12,24的计算次数和12的计算次数是一样,而这个lcm也就是12一定是m的因子,所以只要保证了12计算次数是正确的那么24的计算次数也是正确的
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
#define max_ 200100
#define mod 1000000007
#define inf 0x3f3f3f3f
#define eps 1e-9
int n,m;
int casnum=1;
int cnt[2000],fac[2000];
int tot;
int main(int argc, char const *argv[]) {
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
tot=0;
for(int i=1;i*i<=m;i++)
{
if(m%i==0)
{
fac[++tot]=i;
if(i!=m/i)
{
fac[++tot]=m/i;
}
}
}
sort(fac+1,fac+1+tot);
memset(cnt,0,sizeof cnt);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
x=__gcd(x,m);
for(int j=1;j<=tot;j++)
{
if(fac[j]%x==0)
cnt[j]=1;
}
}
ll ans=0;
for(int i=1;i<=tot;i++)
{
if(cnt[i]==0)
continue;
ans+=(ll)fac[i]*(m/fac[i]+1)*(m/fac[i])/2*cnt[i];
for(int j=i+1;j<=tot;j++)
{
if(fac[j]%fac[i]==0)
{
cnt[j]-=cnt[i];
}
}
}
printf("Case #%d: %lld\n",casnum++,ans-m);
}
return 0;
}