【JZOJ1161】机器人M号(欧拉函数+递推)

Problem

https://jzoj.net/senior/#main/show/1161

Solution

  • 首先要喷一下OJ上的样例输出:明明要输出三行,样例却只输出了一行,害得我也跟着输出了一行,于是被坑掉100points

  • 然后,分析题意。
  • 显然,对于x,若y(y>1且y≠x)为其约数,则y为其老师。而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,则可得:
    n o w = i = 1 e ϕ ( p i )

    n o w = i = 1 e p i 1 ( p 1 )

    n o w = ( p 1 ) i = 0 e 1 p i
  • 推到这里,我们发现:这就一个等比数列求和就解决了!于是继续化简:
    n o w = ( p 1 ) ( p e 1 p 1 )

    n o w = p e 1
  • 于是,我们对于每个p,都令sum(所有约数的欧拉函数和)+=(sum+1)*now。个中的1*now代表的是 p , p 2 , p 3 , . . . , p e 的欧拉函数和;而sum*now则代表的是之前的某个数乘上现在的p的某个次幂所得数的欧拉函数和(因为欧拉函数为积性函数)。譬如p=3,e=4,之前的sum包括了一个2,那么sum*now得到的就是 2 3 , 2 3 2 , 2 3 3 , 2 3 4 这些数的欧拉函数和。

  • 时间复杂度: 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);
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/80972592