ACM-ICPC 2018 沈阳赛区网络预赛 G. Spare Tire
题目
给出 n, m。求下标为1~n内所有与m互质的数的an的总和,
下面是 an 的递推式
a
n
=
{
0
,
n
=
0
2
,
n
=
1
3
a
n
−
1
−
a
n
−
2
2
+
n
+
1
,
n
>
1
a_{n}=\left\{\begin{array}{ll}{0,} & {n=0} \\ {2,} & {n=1} \\ {\frac{3 a_{n-1}-a_{n-2}}{2}+n+1,} & {n>1}\end{array}\right.
an=⎩⎨⎧0,2,23an−1−an−2+n+1,n=0n=1n>1
分析
求 1~n 与 m 互质的个数可以用容斥定理。
但这个题求出的数字只是下标,还要带入数列。先找点数列的性质:
从递推式打表推出通项公式:
a
n
=
n
∗
(
n
+
1
)
a_{n}=n *(n+1)
an=n∗(n+1)
前 n 项和:
S
n
=
n
∗
(
n
+
1
)
∗
(
2
∗
n
+
1
)
6
+
n
∗
(
n
+
1
)
2
S_n = \frac{n *(n+1) *(2 * n+1)}{6}+\frac{n *(n+1)}{2}
Sn=6n∗(n+1)∗(2∗n+1)+2n∗(n+1)
分析求互质的个数时,先找 m 的质因子,然后枚举所有的质因子组合,假如说枚举出来的一个组合为 k。那么 k 的倍数 2k,3k, 4k ……都不与 m 互质,因此先求出数列前 n 项和,减去所有下标不互质的项就是答案。
对于一个组合 k :下标不互质的项也构成了一个数列
b
n
=
(
k
n
)
2
+
k
n
b_{n}=(kn)^2+ kn
bn=(kn)2+kn
前 n 项和:
C
C
n
=
n
∗
(
n
+
1
)
∗
(
2
∗
n
+
1
)
∗
k
2
6
+
n
∗
(
n
+
1
)
∗
k
2
CC_n = \frac{n *(n+1) *(2 * n+1) *k^2}{6}+\frac{n *(n+1)*k}{2}
CCn=6n∗(n+1)∗(2∗n+1)∗k2+2n∗(n+1)∗k
然后用容斥定理算出最后结果即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define d(x) cout<<x<<endl
const int N = 1e4 + 5;
const ll mod = 1e9 + 7;
ll inv2 = 500000004; //2的逆元
ll inv6 = 166666668; //6的逆元
ll n, m;
ll cc(ll n, ll i){
n = n / i; // 算有多少项,
return ( n%mod*(n+1)%mod*(2*n+1)%mod*inv6%mod *i%mod*i%mod
+ n%mod*(n+1)%mod*inv2%mod *i%mod )%mod;
}
int main(){
while(~scanf("%lld%lld", &n, &m)){
vector<int> q;
int tmp = m;
for(int i = 2; i * i <= m; i++){ // 分解 m 质因子到 vector 中
if(tmp%i == 0){
q.push_back(i);
while(tmp%i == 0){
tmp /= i;
}
}
}
if(tmp != 1)
q.push_back(tmp);
int len = q.size();
ll ans = 0;
for (int i = 1; i < (1 << len); i++){ // 二进制枚举左右质因子组合
ll t = 1;
int num = 0;
for (int j = 0; j < len; j++){
if((1<<j) & i){
t *= q[j];
num++;
}
}
t = cc(n, t); // 质因子组合 t 所构成数列的和
if(num&1){ // 奇加偶减
ans = (ans % mod + t % mod) % mod;
}else{
ans = (ans % mod - t % mod + mod) % mod;
}
}
printf("%lld\n", (cc(n, 1) - ans + mod) % mod);
}
return 0;
}
/*
4 4
*/