题目链接:点击这里
题目大意:
有一个长度为m的环,其节点编号为
[
0
,
m
−
1
]
[0,m-1]
[0,m−1] ,有
n
n
n 只青蛙从
0
0
0 号点开始跳,第
i
i
i 只青蛙可以从
p
o
s
pos
pos 跳到
(
p
o
s
+
a
i
)
m
o
d
m
(pos+a_i)\mod m
(pos+ai)modm ,求所有被跳过的点的编号和
题目分析:
首先发现对于第
i
i
i 只青蛙可以跳到的点的编号为
gcd
(
a
i
,
m
)
\gcd(a_i,m)
gcd(ai,m) 的倍数
证明:设青蛙起始位置为
p
o
s
pos
pos ,需要跳
x
x
x 圈,跳了
y
y
y 步,故有等式:
m
x
+
a
i
y
=
p
o
s
mx+a_iy=pos
mx+aiy=pos 由扩展欧几里得易知
p
o
s
pos
pos 最小正整数解为
gcd
(
a
i
,
m
)
\gcd(a_i,m)
gcd(ai,m)
而显然有的点会被重复跳到多次,故会有重复,考虑用容斥原理对
m
m
m 的每一个因子进行计算
如果
m
m
m 的某个因子
f
a
c
fac
fac 对于第
i
i
i 只青蛙的
p
o
s
i
=
gcd
(
a
i
,
m
)
pos_i=\gcd(a_i,m)
posi=gcd(ai,m) 有
f
a
c
m
o
d
p
o
s
i
=
0
fac\mod pos_i=0
facmodposi=0 那么fac的倍数均会被跳到,如果
m
m
m 的某个因子
f
a
c
j
fac_j
facj 会被
m
m
m 的另一个因子整除那么
f
a
c
j
fac_j
facj 的贡献就被重复计算了要减掉
首先对于每一个
x
=
gcd
(
a
i
,
m
)
x=\gcd(a_i,m)
x=gcd(ai,m),如果
m
m
m 的一个因数
f
a
c
m
o
d
x
=
=
0
fac\mod x==0
facmodx==0 ,那么
f
a
c
fac
fac 就会被跳到。
然后对于每一个会碰到的因数计算,当
m
m
m 的一个因数j的因数
i
i
i 被计算的时候,
j
j
j 就会被重复计算,要减去。
故此题复杂度为
O
(
n
2
)
O(n^2)
O(n2) ,
n
n
n 为
m
m
m 的因子个数,而
1
e
9
1e9
1e9 内最多的一个数的因子个数是
1536
1536
1536 个,所以这个题目的时间复杂度也只是 1e6 级别的,不会超时(如果直接对每个
p
o
s
i
pos_i
posi 进行容斥原理时间复杂度为
O
(
2
n
)
O(2^n)
O(2n) 直接
t
t
t 飞)
具体细节见代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e5+5;
int fac[maxn],cnt;
int ok[maxn];//ok[i]为第i个因子的贡献
void cal(int num)//计算因子
{
cnt = 0;
fac[++cnt] = 1;
for(int i = 1;i*i <= num;i++)
{
if(i*i == num)
{
fac[++cnt] = i;
break;
}
if(num%i == 0)
{
fac[++cnt] = i;
fac[++cnt] = num/i;
}
}
sort(fac+1,fac+cnt+1);
}
int main()
{
int t = read(),cas = 0;
while(t--)
{
int n = read(),m = read();
cal(m);
memset(ok,0,sizeof(ok));
for(int i = 1;i <= n;i++)
{
int ai = read();
int gcd = __gcd(ai,m);
for(int j = 1;j < cnt;j++)
if(fac[j]%gcd == 0) ok[j] = 1;
}
ll ans = 0;
for(int i = 1;i < cnt;i++)
{
if(!ok[i]) continue;
ll tmp = (m-1)/fac[i];
ans += tmp*(tmp+1)/2*fac[i]*ok[i];
for(int j = i+1;j <= cnt;j++)
{
if(fac[j]%fac[i] == 0) ok[j] -= ok[i];
}
}
printf("Case #%d: %lld\n",++cas,ans);
}
return 0;
}