Problem
https://jzoj.net/senior/#main/show/1161
Solution
- 首先要喷一下OJ上的样例输出:明明要输出三行,样例却只输出了一行,害得我也跟着输出了一行,于是被坑掉100points!
- 然后,分析题意。
- 显然,对于x,若y(y>1且y≠x)为其约数,则y为其老师。而x的独立数则显然为欧拉函数 ϕ(x) ϕ ( x ) 。
- 于是,题目被转化为:给定m,求其所有大于1的约数中,政客、军人、老师的欧拉函数和。
- 然后,看一下政客和军人的定义。显然它们都不能有相同的质因子;换句话说,每个质因子p至多出现一次。
- 容易想到
o(2^k)暴力组合递推。记zk[i]、jr[i]分别表示处理到第i个质因子时政客、军人的答案,则可得递推式:
zk[i]=(zk[i-1]+jr[i-1]*(p-1))%M;//式1
jr[i]=(jr[i-1]+(zk[i-1]+1)*(p-1))%M;//式2
- 因为 ϕ ϕ 为积性函数,而政客有偶数个质因子,军人有奇数个,故政客的递推式为式1;而军人因为有奇数个质因子,不必由政客得来,可以无中生有,故军人的递推式为式2。
- 关键就在计算学者了。
- 直接计算不太好求。可以先求出总和,再减去政客和军人的和。
- 于是,原问题只剩下一个了:给定m,求其所有大于1的约数的欧拉函数和。
- 设当前的质因子为p,指数为e,对答案的贡献为now,则可得:
now=∑i=1eϕ(pi) n o w = ∑ i = 1 e ϕ ( p i )
now=∑i=1epi−1∗(p−1) n o w = ∑ i = 1 e p i − 1 ∗ ( p − 1 )
now=(p−1)∗∑i=0e−1pi n o w = ( p − 1 ) ∗ ∑ i = 0 e − 1 p i - 推到这里,我们发现:这就一个等比数列求和就解决了!于是继续化简:
now=(p−1)∗(pe−1p−1) n o w = ( p − 1 ) ∗ ( p e − 1 p − 1 )
now=pe−1 n o w = p e − 1 - 于是,我们对于每个p,都令sum(所有约数的欧拉函数和)+=(sum+1)*now。个中的1*now代表的是 p,p2,p3,...,pe p , p 2 , p 3 , . . . , p e 的欧拉函数和;而sum*now则代表的是之前的某个数乘上现在的p的某个次幂所得数的欧拉函数和(因为欧拉函数为积性函数)。譬如p=3,e=4,之前的sum包括了一个2,那么sum*now得到的就是 2∗3,2∗32,2∗33,2∗34 2 ∗ 3 , 2 ∗ 3 2 , 2 ∗ 3 3 , 2 ∗ 3 4 这些数的欧拉函数和。
- 时间复杂度: O(k∗log2e) O ( k ∗ l o g 2 e ) 。
Code
#include <cstdio>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int K=1001,M=1e4;
int i,k,p,e,zk[K],jr[K],sum,now;
bool bz;
int ksm(int x,int y)
{
ll ans=1;
for(;y;y>>=1)
{
if(y&1) ans=ans*x%M;
x=x*x%M;
}
return ans;
}
int main()
{
scanf("%d",&k);
fo(i,1,k)
{
scanf("%d%d",&p,&e);
if(bz) zk[i]=(zk[i-1]+jr[i-1]*(p-1))%M;
if(p>2)
{
jr[i]=(jr[i-1]+(zk[i-1]+1)*(p-1))%M;
bz=1;
}
now=(ksm(p,e)-1+M)%M;
sum=(sum+(sum+1)*now)%M;
}
printf("%d\n%d\n%d",zk[k],jr[k],(sum-zk[k]-jr[k]+2*M)%M);
}