[Noi2002]Robot 欧拉函数+递推

本文针对BZOJ1408题目提供了解题思路与代码实现,通过递推公式计算一类和二类约数的欧拉函数之和,并结合欧拉函数性质快速得出三类约数结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:bzoj1408

—————————————-

概述

题目的意思比较复杂,大致如下:

给定一个数n,将它所有约数分为三类:

  1. 能分解成偶数个不同奇质数的乘积.(质因数的指数小于等于1)
  2. 能分解成奇数个不同奇质数的乘积.(质因数的指数小于等于1)
  3. 不满足1、2的约数.

注:1不算任何数的约数.

例如:对于正整数90:

  • 一类约数:15.
  • 二类约数:3、5.
  • 三类约数:2、6、9、10、18、30、45、90.

现在告诉你正整数n,让你求得其一类约数的欧拉函数之和,二类约数的欧拉函数之和,三类约数的欧拉函数之和。结果均对10000取模。

我们将 n 质因数分解:

n=i=1kpeii.

题目将给出 k ,以及n所有的质因数 pi 和其指数 ei .

—————————————-

题解

我们发现,上面三类约数的并集很显然就是 n 的所有约数(不包括1)

由于正整数n的欧拉函数有这么一条性质:

n=d|nφ(d).

(不清楚的可以参考我之前的博客:常见函数讲解1:欧拉函数,其中“扩展性质”中给出了上式的证明)

所以,我们设三类约数的欧拉函数之和分别为 Ans1Ans2Ans3 ,那么显然有:

Ans1+Ans2+Ans3=n1.

也就是说,假如我们求得了 Ans1Ans2 ,那 Ans3 自然能得知了。

那么我们如何求得 Ans1 Ans2 呢?

我们发现,对于一个一类约数 a=mi=1pi ,其中 m 为偶数,我们对a乘一个不属于 {p1 ~ pm} 的质因数 pm+1 ,那么我们就能把一类约数 a 变成二类约数a×pm+1.

我们顺着这个思路想,我们用前 i1 个质因数构成的一类约数,只要乘上 pi 那么就能把他们全都变成二类约数了。

s1i 为前 i 个质因数构成的一类约数的欧拉函数之和,s2i为前 i 个质因数构成的二类约数的欧拉函数之和。由上面得到的结论,我们可以推出s1 s2 之间存在如下的递推关系:

s1i=s2i1×φ(pi).
s2i=s1i1×φ(pi)+φ(pi).

很明显, Ans1=s1k, Ans2=s2k 。所以我们只要初始化 s10=s20=0 ,然后 O(n) 的递推即可得到 Ans1 Ans2 的值。

至于 Ans3 嘛,之前得出了 Ans3=n1Ans1Ans2 这样的结论,直接计算即可。别忘了取模。

—————————————-

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#define ll long long
#define For(i,j,k) for(register int i=j; i<=(int)k; ++i)
#define Forr(i,j,k) for(register int i=j; i>=(int)k; --i)
#define INF 0x3f3f3f3f
using namespace std;

const int mo = 10000;
const int maxn = 1000+5;

int n, x, Ans1, Ans2, Ans3;
int p[maxn], e[maxn];

inline int ksm(int x, int y){
    int back = 1;
    x %= mo;
    while(y){
        if(y & 1)   back = back*x%mo;
        x = x*x%mo;
        y >>= 1;
    }
    return back;
}

int main(){
    scanf("%d", &n);
    For(i, 1, n)    scanf("%d%d", &p[i], &e[i]);

    int s1, s2;
    For(i, 1, n){
        if(p[i] == 2)   continue;
        s1 = (Ans1 + Ans2*(p[i]-1)%mo)%mo;
        s2 = ((Ans2+p[i]-1)%mo + Ans1*(p[i]-1)%mo)%mo;
        Ans1 = s1,  Ans2 = s2;//递推求得Ans1和Ans2. 
    }

    x = 1;
    For(i, 1, n)
        x = x*ksm(p[i], e[i])%mo;//计算n在模10000意义下的值. 
    Ans3 = (x-Ans1-Ans2-1)%mo;//计算Ans3. 
    while(Ans3 < 0) Ans3 += mo;//防止Ans3爆负. 

    printf("%d\n%d\n%d", Ans1, Ans2, Ans3);
    return 0;
}

—————————————-

小结

本题的关键在于找到那个递推式计算 Ans1 Ans2 ,结合欧拉函数的性质就能快速计算Ans3。作者想了1h小时无果,膜拜大佬之后便有了灵感。果然,分析性质这儿技能还需要多锻炼才能提高。

—————————————-

wrote by miraclejzd.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值