いくつかの数論およびその他の問題の解決策

P3601 チェックインに関する質問

トピックの背景

関数を定義します。qiandao(x) は、x 以下であり、x と素数ではない数値の数です。

この質問はサインインの質問として使用されます。l と r が与えられると、次の式の結果を見つける必要があります。

∑ i = lrqiandao ( x ) mod 666623333 \sum_{i=l}^r qiandao(x)\ mod \ 666623333= lrq i and a o ( x ) mod 6 6 6 6 2 3 3 3 3 _ _ _ _  

入力フォーマット

2 つの整数 l、r の行。

出力フォーマット

1 行につき 1 つの整数が答えを表します。

指示/ヒント

データの 30% については、l , r ≤ 1 0 3 l,r\leq 10^3l r1 03

データの 60% については、l , r ≤ 1 0 7 l,r\leq 10^7l r1 07

データの 100% について、1 ≤ l ≤ r ≤ 1 0 12 、 r − l ≤ 1 0 6 1\leq l \leq r \leq 10^{12}, rl \leq 10^61r1 01 2r1 06

答え

タイトルから、qiandao ( x ) = x − ϕ ( x ) qiandao(x)=x−\phi(x) であることがわかります。
q i and a o ( x ) _ _=x ϕ ( x )
ここで、ϕ ( x ) \phi(x)ϕ ( x )はオイラー関数です。
正の整数 n の場合、オイラー関数は、n 以下の正の整数の中で n と互いに素な数の数です。
ϕ ( x ) = ∏ i = 0 kn ( 1 − 1 pi ) \phi(x)=\prod_{i=0}^kn(1-\frac1{p_i})ϕ ( x )=i = 0n ( 1p私は1)
其中 p i p_i p私はnnのためにnの素因数。
各素因数pi p_ip私はピピp私はnを npi \frac n{p_i}に分割できます。p私はパーツ、各パーツには素因数pi p_iが含まれる数値が 1 つだけありますp私はつまり、品質係数pi p_i は1~n から抽出されます。p私はその数の確率は1 pi \frac 1{p_i}ですp私は1したがって、n を含まない素因数の確率は∏ i = 0 k ( 1 − 1 pi ) \prod_{i=0}^k(1-\frac1{p_i}) となります。i = 0( 1p私は1)、確率に n を乗じた値は、互いに素な数の数になります。より正式な説明は、包含と除外の原則しかし本質的には確率で説明するのと何ら変わりはなく、ベン図で推論することができます。

オイラー関数を理解した後は、問題は簡単です。
通常、オイラーを使用して素数をふるい分けると、オイラー関数を見つけることができます。
次の式を使用します。
ϕ ( p ∗ y ) = { p ϕ ( y ) y には因子 p ( p − 1 ) が含まれます ϕ ( y ) y には因子 p は含まれません \phi(p*y)= \left\{ \begin {array }{rcl} p\ \phi(y) & y には因子 p が含まれます & \\ (p-1) \phi(y) & y には因子 p が含まれません & \\ \end{array} \right。ϕ ( py )={ p ϕ ( y ) ( p1 ) ϕ ( y )yには因数pがありますy には因数p がありません
ただし、この問題のrの範囲は大きすぎ、rlの範囲は小さすぎてオイラー関数を直接計算することはできません。オイラー関数の値を更新するには、 √rの範囲内の素数を使用する必要があります。l~rの範囲。なお、残りの数が1でない場合は √rより大きい素数がまだ残っており、再度処理する必要があることを意味する。

コード

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define MOD 666623333
#define phi(x) _phi[(x)-l]
#define remain(x) _remain[(x)-l]

const int N = 1e6 + 5;
ll l, r;
ll prime[N], _phi[N], _remain[N], tot;
bool notPrime[N];
void EulerSieve(ll siz) {
    
    
  for (ll i = 2; i < N; i++) {
    
    
    if (!notPrime[i]) prime[tot++] = i;
    for (int j = 0; j < tot && i * prime[j] < N; j++) {
    
    
      notPrime[i * prime[j]] = true;
      if (i % prime[j] == 0) break;
    }
  }
}
int main(int argc, char const *argv[]) {
    
    
  ios::sync_with_stdio(false), cin.tie(0);
  cin >> l >> r;
  EulerSieve(0);
  for (ll i = l; i <= r; i++) remain(i) = phi(i) = i;
  for (int i = 0; prime[i] * prime[i] <= r; i++) {
    
    
    ll p = prime[i];
    for (ll j = ((l + p - 1) / p) * p; j <= r; j += p) {
    
    
      phi(j) = phi(j) / p * (p - 1);
      while (remain(j) % p == 0) remain(j) /= p;
    }
  }
  ll ans = 0;
  for (ll i = l; i <= r; i++) {
    
    
    if (remain(i) != 1) {
    
    
      phi(i) = phi(i) / remain(i) * (remain(i) - 1);
    }
    ans = (ans + i - phi(i)) % MOD;
  }
  cout << ans;
  return 0;
}

P1593 要因と項目の説明

2 つの整数 a と b を入力し、aba^bを求めます。あるbの因数和

結果が大きすぎるため、モジュロ 9901 の結果を出力するだけです。

入力フォーマット

2 つの整数 a と b については 1 行のみ。

出力フォーマット

9901 を法とする答えの結果を表す整数を含む行を出力します。

指示/ヒント

すべてのテスト ポイントについて、1 ≤ a ≤ 5 × 1 0 7 、0 ≤ b ≤ 5 × 1 0 7 1 \leq a \leq 5 \times 10^7,0 \leq b \leq 5 \times 10^ 7 を保証します。1ある5×1 070b5×1 07

答え

数字xxの場合x は素因数の積に分解できます
x = p 1 k 1 p 2 k 2 . . . pnknx=p_1^{k_1}p_2^{k_2}...p_n^{k_n}バツ=p1k1p2k2pnk
f ( x ) f(x)を定義します。f ( x )はxxですxの因子和
、f ( x ) = ∏ i = 1 n ∑ k = 0 kipikf(x)=\prod_{i=1}^n\sum_{k=0}^{k_i} p_i^kf ( x )=i = 1k = 0k私はp
それを証明しましょう

y = p 2 k . . . . . . . . pnkny=p_2^{k_2}...p_n^{k_n}y=p2k2pnkyyを見つけることができますyのすべての因数p 1 p_1p1p 1 k 1 − 1 p_1^{k_1-1}を掛けますp1k1 12 つのペアの組み合わせはたまたまxxになりますxなどの係数にp 1 2 p_1^2を乗算します。p12そしてp 1 k 1 − 2 p_1^{k_1-2}p1k1 2ペアリングも可能です。
すると、以下の漸化式が得られます。
f ( x ) = f ( y ) ∑ k = 0 k 1 p 1 kf(x)=f(y)\sum_{k=0}^{k_1} p_1^kf ( x )=f ( y )k = 0k1p1
明らかに、解凍を続けると、最初の式が完成します。
次に、等比数列の合計公式を使用して
f ( x ) = ∏ i = 1 npiki + 1 − 1 pi − 1 f(x)=\prod_{i=1}^n \frac{p_i^{k_i+1} を取得します。 -1}{p_i-1}f ( x )=i = 1p私は1pk私は+1 _1
对于 p i k i + 1 p_i^{k_i+1} pk私は+1 _割り算pi − 1 p_i-1の場合、高速なべき乗で解きます。p私は1、結果はモジュロである必要があるため、pi − 1 p_i-1p私は1を pi − 1 p_i-1で乗算するように変更します。p私は1の逆数

フェルマーの小定理

逆元の定義とフェルマーの小定理によると ( a は p の倍数ではありません)
a ∗ x ≡ 1 ( mod p ) ap − 1 ≡ 1 ( mod p ) a∗x \equiv 1(mod\ p) \ \ a^{ p-1}≡1(mod\ p)a x1 ( mod p ) _ _ あるp 11 ( mod p )明らか 
aa _a a p − 2 a^{p-2} あるp 2は互いに逆数です。ここまでは基本的に解決しました。
特に a が p の倍数の場合。pi − 1 ≡ 0 ( mod 9901 ) p_i-1\equiv0 (mod\ 9901 )p私は10 ( mod 9 9 0 1 )イコール pi ≡ 1 ( mod 9901 ) p_i\equiv1 (mod\
9901 )p私は1(mod 9901)
代入原求和公式
s u m = ( p i   m o d   9901 ) 0 + ( p i   m o d   9901 ) + . . . + ( p i   m o d   9901 ) k i s u m = k i + 1 sum=(p_i\ mod\ 9901)^0+(p_i\ mod\ 9901)+...+(p_i\ mod\ 9901)^{k_i} \\ sum=k_i+1 うーん_ _=( p私は mod 9 9 0 1 ) _ _ 0+( p私は mod 9 9 0 1 ) _ _ ++( p私は mod 9 9 0 1 ) _ _ k私はうーん_ _=k私は+1

拡張ユークリッド アルゴリズム

上記の逆元を求める方法に加えて、拡張ユークリッド アルゴリズムを使用することもできます。
ユークリッド アルゴリズムの目的は、gcd ( a , b ) = ax + by gcd(a,b)=ax+by を解くことです。g c d ( a ,b )=× _+xxまでxyyy
x と a が mod p の意味で逆数である場合。すると、次の方程式
ax + kp = 1 ax+kp=1が成り立ちます。× _+kp _=1
以下は、拡張ユークリッド アルゴリズムが
次の 2 つの式を持つこと
を証明します。 ax + by = gcd ( a , b ) bx ' + ( a mod b ) y ' = gcd ( b , a mod b ) ax+by=gcd( a ,b) \\ bx'+(a\ mod\ b) y'=gcd(b,a\ mod\ b) \\× _+によって_=g c d ( a ,b )bx _+( a mまたはdb )および_  =g c d ( b ,a mod b ) _ _  
又因としてgcd ( b , a mod b ) = gcd ( a , b ) gcd(b,a\ mod\ b)=gcd(a,b)g c d ( b ,a mod b ) _ _  =g c d ( a ,b )
bx ' + ( a mod b ) y ' = ax + by 即 : { x = y ' y = x ' − ⌊ ab ⌋ y ' bx'+(a\ mod\ b) y'=ax+by \ \ 即時: \\ \left\{ \begin{array}{l} x=y' & \\ y=x'-\lfloor \frac ab \rfloor y' & \\ \end{array} \right。bx _+( a mまたはdb )および_  =× _+によって_たった今:{ バツ=yy=バツby
したがって、x と y を解くには、 x ' x'を解くだけで済みます。バツそして 」y' 、最終的に b = 0 、x = 1、y = 0 の場合の b = 0 、x = 1、y = 0 の場合の再帰境界を見つけます。いつb=0 バツ=1 y=0

コード

#include <bits/stdc++.h>
using namespace std;
const int N = 5e7 + 5;
int a, b;
#define MOD 9901
void getFactors(int x, vector<pair<int, int>> &res) {
    
    
  for (int i = 2; i * i <= x; i++) {
    
    
    if (x % i == 0) {
    
    
      res.push_back({
    
    i, 0});
      while (x % i == 0) {
    
    
        res.back().second++;
        x /= i;
      }
    }
  }
  if (x != 1) {
    
    
    res.push_back({
    
    x, 1});
  }
}
int quick_pow(int p, int x) {
    
    
  p %= MOD;
  int ans = 1;
  while (x) {
    
    
    if (x & 1) {
    
    
      ans *= p;
      ans %= MOD;
    }
    p = (p * p) % MOD;
    x >>= 1;
  }
  return ans;
}
int main(int argc, char const *argv[]) {
    
    
  ios::sync_with_stdio(false), cin.tie(0);
  cin >> a >> b;
  vector<pair<int, int>> f1;
  getFactors(a, f1);
  int ans = 1;
  for (int i = 0; i < f1.size(); i++) {
    
    
    int p = f1[i].first;
    // p^0 + p^1 + ... + p^(n-1)
    int n = f1[i].second * b + 1;
    if (p % MOD == 1) {
    
    
      ans *= n, ans %= MOD;
    } else {
    
    
      // 求 p-1 逆元 还可用扩展欧几里得算法
      int inverse = quick_pow(p - 1, MOD - 2);
      ans *= inverse, ans %= MOD;
      ans *= ((quick_pow(p, n) - 1 + MOD) % MOD), ans %= MOD;
    }
  }
  cout << ans;
  return 0;
}

P1069 細胞分裂のトピックの説明

ハンクス博士は、BT (Bio−Tech Biotechnology) の分野では有名な専門家です。現在、彼は細胞実験の準備をしており、細胞のサンプルを増殖させています。

ハンクス博士は現在、1 から N まで番号が付けられた N 種類の細胞を持っており、タイプ i の細胞は1 秒後にS i S_iに分裂できます。S私は同じ種類の細胞です。次に、特定の種類の細胞から 1 つを選択し、それをペトリ皿に入れて、培養のために自由に分裂できるようにする必要があります。一定の時間が経過した後、ペトリ皿内のすべての細胞を M 個の試験管に分割し、実験用の M 個のサンプルを作成します。ハンクス博士の試験管 M の数は非常に多く、通常のコンピューターの基本データ型ではそのような大きな値 M を格納できませんが、幸いなことに MMM は常にm 1 m_1として表現できます。メートル1メートル2メートル_2メートル2電力、つまりM = m 1 m 2 M = m_1^{m_2}M=メートル1メートル2

ここで医師は、実験をできるだけ早く開始できるように、どの細胞培養を選択すればよいかを知りたいと考えています。

入力フォーマット

最初の行には、セルの種類の数を表す正の整数 N があります。

2 行目には、2 つの正の整数m 1 、m 2 m_1、m_2があります。メートル1メートル2スペースで区切られています。これは試験管の総数を意味しますM = m 1 m 2 M = m_1^{m_2}M=メートル1メートル2

3 行目には N 個の正の整数があり、i 番目の数値S i S_iS私は1秒後に同じ種類のセルに分割できるi番目の種類のセルの数を示します。

出力フォーマット

細胞が培養されてから実験を開始できるようになるまでの最小時間 (秒単位) を表す整数。

ハンクス博士がどのセルを選択しても要件が満たされない場合は、整数 -1 を出力します。

答え

この問題の直接因数分解は非常に簡単です。
ただし、最大公約数を繰り返し抽出し、互いに素な部分を生成し、それを削除する別の方法もあります。少し複雑なのは、最後の再帰終了の状況です。この割り算の判断方法について言えば、高校の数学の先生も言っていましたが、以前はなんとも思わなかったのですが、今では高校の数学の先生の方が優れていると感じることが多くなりました。

#include <bits/stdc++.h>
using namespace std;
#define N 30005
int p[N], n, m1, m2, top;
int np1[N], np2[N];
bool vis[N];
void prime_factorization(int x, int res[]) {
    
    
  memset(res, 0, sizeof(res[0]) * top);
  for (int j = 0; j < top && p[j] <= x; j++) {
    
    
    while (x % p[j] == 0) x /= p[j], res[j]++;
  }
}

int main(int argc, char const *argv[]) {
    
    
  ios::sync_with_stdio(false), cin.tie(0);
  cin >> n;
  cin >> m1 >> m2;
  for (int i = 2; i <= m1; i++) {
    
    
    if (!vis[i]) p[top++] = i;
    for (int j = 0; j < top && i * p[j] <= m2; j++) {
    
    
      vis[i * p[j]] = true;
      if (i % p[j] == 0) break;
    }
  }
  if (m1 == 1 || m2 == 0) {
    
    
    cout << "0";
    return 0;
  }
  prime_factorization(m1, np1);
  int ans = INT_MAX;
  for (int i = 0, t; i < n; i++) {
    
    
    cin >> t;
    prime_factorization(t, np2);
    int t_ans = -1;
    for (int j = 0; j < top; j++) {
    
    
      if (np1[j] == 0) continue;
      if (np2[j] == 0) {
    
    
        t_ans = -1;
        break;
      }
      t_ans = max((np1[j] * m2 + np2[j] - 1) / np2[j], t_ans);
    }
    if (t_ans >= 0) ans = min(ans, t_ans);
  }
  cout << (ans == INT_MAX ? -1 : ans);
  return 0;
}

P2671 合計トピックの説明

細長い紙を均等に n 個のグリッドに分割し、各グリッドに 1 から n までの番号を付けます。各グリッドは色colori color_iで染められます。_ _ _ _私は[ 1 , m ] [1, m]の場合[ 1 m ] は整数を表します)、数値numberinumber_i番号_ _ _ _ _私は

特別な 3 つ組を定義します: (x, y, z), x, y, z はすべて紙テープ上のグリッドの番号を表します。ここでの 3 つ組は次の 2 つの条件を満たす必要があります: xyz は整数、x <
y < z , y − x = z − ycolorx = colorz xyz は整数、x<y<z、yx=zy \\ color_x=color_zxy z整数ですバツ<y<z yバツ=zy_ _ _ _×=_ _ _ _z
上記の条件を満たすトリプルの小数は、( x + z ) × (numberx +numberz ) (x+z) \times (number_x+number_z)として指定されます。( ×+z )×(番号_ _ _ _ _×+番号_ _ _ _ _zテープ全体のスコアは、条件を満たすすべてのトリプルのスコアの合計として指定されます。この端数は非常に大きくなる場合があります。テープ全体の端数を出力し、それを10 , 007 10,0071 0 0 0 7から得られる余り

入力フォーマット

最初の行は、スペースで区切られた 2 つの正の整数 n と m です。n は紙テープのグリッドの数を表し、m は紙テープの色の数を表します。

2 行目にはスペースで区切られた n 個の正の整数があり、i 番目の数値は i 番のグリッドに書かれた数値です。

3 行目にはスペースで区切られた n 個の正の整数があり、i 番目の数値の色は、表の用紙上の i 番目の数値グリッドの色です。

出力フォーマット

目的のテープ部分を 10007 で割った余りを表す整数。

答え

この質問では、x と y は両方とも奇数か偶数であることが明らかです。そこで、次のテーブルを色とパリティで分類し、最後にトラバースして 2 つを選択してスコアを計算します。
しかし、この質問のデータは比較的大きいO ( n 2 ) O(n^2)です。O ( n2 )タイムアウトになります。
しかし、O(n) 時間の計算量に削減できる次の式があります。

下面推导一下下面公式
∑ i = 1 n ∑ j = i + 1 n ( a i + a j ) ( b i + b j ) = ( n − 2 ) ∑ i = 1 n a i b i + ∑ i = 1 n a i ∑ j = 1 n b j \sum_{i=1}^n \sum_{j=i+1}^n (a_i+a_j)(b_i+b_j) =(n-2)\sum_{i=1}^n a_ib_i +\sum_{i=1}^n a_i \sum_{j=1}^n b_j i = 1j = i + 1( _私は+あるj) ( b私は+bj=( n2 )i = 1ある私はb私は+i = 1ある私はj = 1bj
恥ずかしながらa_ib_iある私はb私は,可由 ( a i + a j ) ( b i + b j ) (a_i+a_j)(b_i+b_j) ( _私は+あるj) ( b私は+bj)が生成されます。ここで、jji ≠ ji \neq jである限り、 j は自由です=j個の制約があるため、合計でn − 1 n-1 個なります。n1 jj_jが選択可能です。つまり( n − 1 ) aibi (n-1)a_ib_i( n1 ) _私はb私は,对于 a i a j 且 i ≠ j a_ia_j且i \neq j ある私はあるji=j两个变量都固定,则只有一个。写成公式如下
∑ i = 1 n ∑ j = i + 1 n ( a i + a j ) ( b i + b j ) = ( n − 1 ) ∑ i = 1 n a i b i + ∑ i = 1 n a i ∑ j ∈ { x ∣ x ∈ N + , x ≤ n , x ≠ i } b j \sum_{i=1}^n \sum_{j=i+1}^n (a_i+a_j)(b_i+b_j) =(n-1)\sum_{i=1}^n a_ib_i +\sum_{i=1}^n a_i \sum_{j\in \{x|x\in N^+,x\le n,x\neq i\}} b_j i = 1j = i + 1( _私は+あるj) ( b私は+bj=( n1 )i = 1ある私はb私は+i = 1ある私はj { x x N+xnx=}bj

#include <bits/stdc++.h>
using namespace std;
#define N 100005
#define MOD 10007
int m, n, ans = 0;
int nums[N];
vector<int> colors[N][2];

void solve(const vector<int> &ids) {
    
    
  int n = ids.size();
  if (n < 2) return;
  int sum = 0;
  for (int i = 0; i < n; i++) {
    
    
    sum += nums[ids[i]];
    sum %= MOD;
  }
  int ab = 0, tmp = 0;
  for (int i = 0; i < n; i++) {
    
    
    int id = ids[i];
    ab += ((id % MOD) * (nums[id] % MOD));
    ab %= MOD;
    tmp += (id * sum);
    tmp %= MOD;
  }
  ab *= (n - 2), ab %= MOD;
  ans += tmp, ans %= MOD;
  ans += ab, ans %= MOD;
}
int main(int argc, char const *argv[]) {
    
    
  ios::sync_with_stdio(false), cin.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    
    
    cin >> nums[i];
  }
  for (int i = 1, t; i <= n; i++) {
    
    
    cin >> t;
    colors[t][i & 1].push_back(i);
  }
  for (int i = 1; i <= m; i++) {
    
    
    solve(colors[i][0]);
    solve(colors[i][1]);
  }
  cout << ans;
  return 0;
}

おすすめ

転載: blog.csdn.net/qq_45256489/article/details/124633225