yukicoder No.28 - 末尾最適化

Source

ニコニコミュニティ
問題文

問題概要

長さ $N+1$ の正整数列 $\{X_k\}_{k=0}^N$ が与えられる(実際には乱数生成方法と種が与えられる).
その中から,$K$ 個の要素を選び,その積を $B$ 進数で表した時,末尾の $0$ の個数を最小化したい.
うまく $K$ 個の整数を選んだ時の,最小値を求める問題.

解法

$B = p_1^{m_1} p_2^{m_2} \cdots p_f^{m_f}$ と素因数分解する.
$z$ が持つ素因数 $p_k$ の個数を $s_k$ とする($z$ は $p_k^{s_k}$ で割り切れる)とすると,$z$ を $B$ 進数で書いた時の末尾の $0$ の数は $\displaystyle \min_k \lfloor m_k/s_k\rfloor$ となる.
これを最小化したいのだから,ボトルネックとなる $k$ を全て試せば良い.
つまり,$\{X_k\}_{k=0}^N$ の要素のうち,$p_k$ で割り切れる回数が少ない方から $K$ 個を選ぶというのを全ての $k$ に対して試す.

C++によるスパゲッティなソースコード

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

#define REP(i,a,b) for(i=a;i<b;i++)
#define rep(i,n) REP(i,0,n)

#define ll long long
#define ull unsigned ll

#define INF 1000000000

int Q;
int seed, N, K, B;
int X[100000];
int Y[100000];

int p[20], pn[20], ps;

int main(){
  int i, j, k;
  int res;
  
  scanf("%d",&Q);
  while(Q--){
    scanf("%d%d%d%d",&seed,&N,&K,&B);
    N++;
    
    X[0] = seed;
    REP(i,1,N) X[i] = 1 + (((ll)X[i-1]*X[i-1] + (ll)X[i-1]*12345) % 100000009);

    ps = 0;
    for(i=2;i<=B;i++){
      if(B%i==0){
        p[ps] = i;
        pn[ps] = 0;
        while(B%i==0) pn[ps]++, B/=i;
        ps++;
      }
    }

    res = INF;
    rep(k,ps){
      rep(i,N){
        Y[i] = 0;
        j = X[i];
        while(j%p[k]==0) j/=p[k], Y[i]++;
      }
      sort(Y, Y+N);
      j = 0;
      rep(i,K) j += Y[i];
      res = min(res, j/pn[k]);
    }

    printf("%d\n",res);
  }

  return 0;
}

Current time: 2017年11月18日04時27分22秒
Last modified: 2014年11月06日13時04分11秒 (by laycrs)
Tags: Competitive_Programming yukicoder
トップページに戻る

Logged in as: unknown user (not login)

ログイン: