1. 程式人生 > >NOIP模擬:切蛋糕(數學歐拉函數)

NOIP模擬:切蛋糕(數學歐拉函數)

phi span ret -s return 求一個 文件 要求 多個

題目描述

  BG 有一塊細長的蛋糕,長度為 n。
  有一些人要來 BG 家裏吃蛋糕, BG 把蛋糕切成了若幹塊(整數長度),然後分給這些人。
  為了公平,每個人得到的蛋糕長度和必須相等,且必須是連續的一段。
  但是, BG 並不知道要有多少人來。 他只知道, 來的人數為
n的約數,且小於n。

  顯然把蛋糕平均分成 n 塊一定能滿足要求。但是, BG 想要分出的塊數盡量少。現在 BG
  想知道,他要把蛋糕分成至少多少塊,才能使得不管多少人來都能滿足要求。

輸入格式

  輸入文件名為 cake.in
  輸入共一個整數 n,
表示蛋糕的長度。

輸出格式

  輸出文件名為 cake.out

  輸出共一個整數, 表示分出的最少塊數。

樣例輸入1

  6

樣例輸出1

  4

樣例輸入2

  15

樣例輸出2

  7

題目分析

  拿15的分割為例子:

   技術分享

   可以看出切割處均為15的約數(在15處會出現重疊),

  若設 f(x) 表示15中x的倍數有幾個,則答案應為

  $$ans = f(3) + f(5) - f(15) $$

  其實就是n - 小於n且與n互質的數---->歐拉函數

  歐拉函數$\phi$(n) : $\phi$(n) 表示[1, n]中與 n 互質的整數的個數。

  主要公式: $$phi(n) = n · prod_{p \in P} \frac{p - 1}{p}$$

  歐拉函數有兩種求法:

  • 多個數的歐拉函數
void sieve() {
    phi[1] = 1;
    for (int i = 2; i < N; ++i) {
        if (!pr[i])
            prime[pn++] = pr[i] = i, phi[i] = i - 1;
        for (int j = 0; j < pn; ++j) {
            int k = i * prime[j];
            if (k >= N) break;
            pr[k] 
= prime[j]; if (i % prime[j] == 0) { phi[k] = phi[i] * prime[j]; break; } else phi[k] = phi[i] * (prime[j] - 1); } } }

  • 求一個數的歐拉函數
    p = ans = n;
    for(int i = 2; i * i <= p; i++){
        if(p % i == 0) ans = ans / i * (i - 1) ;
        while(p % i == 0) p /= i;
    }
    if(p != 1)
        ans = ans / p *(p - 1) ;

  本題只需求一個值。

CODE

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;

int phi;
int ans;
int n, p;

int main(){
    cin>>n;
    p = ans = n;
    for(int i = 2; i * i <= p; i++){
        if(p % i == 0) ans = ans / i * (i - 1) ;
        while(p % i == 0) p /= i;
    }
    if(p != 1)
        ans = ans / p *(p - 1) ;
    cout<<n - ans;
    return 0;
}

NOIP模擬:切蛋糕(數學歐拉函數)