Cod sursa(job #653469)

Utilizator daniel.dumitranDaniel Dumitran daniel.dumitran Data 28 decembrie 2011 02:01:51
Problema Numere 2 Scor 95
Compilator cpp Status done
Runda Arhiva de probleme Marime 6.36 kb
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <stdint.h>
#include <string>

#include <stdint.h>
#include <string>

template<int N> struct Pow10 { static const uint64_t pow = 10 * Pow10<N-1>::pow; };
template<> struct Pow10<0> { static const uint64_t pow = 1; };

class BigInt {
public:
  explicit BigInt(uint64_t x = 0) {
    m_len = 10;
    memset(m_cif, 0, sizeof(m_cif));
    for (uint32_t i = 0; x; ++i) {
      m_cif[i] = x % sm_base;
      x /= sm_base;
    }
    fix();
  }

  explicit BigInt(const std::string& number) {
    const char* digits = number.c_str();
    m_len = 0;
    memset(m_cif, 0, sizeof(m_cif));    

    char buff[sm_base_pow + 5];

    int poz = strlen(digits) - sm_base_pow;
    while (poz >= 0) {
      memset(buff, 0, sizeof(buff));
      strncpy(buff, digits + poz, sm_base_pow);
      m_cif[m_len++] = atoi(buff);
      poz -= sm_base_pow;
    }
    
    memset(buff, 0, sizeof(buff));
    strncpy(buff, digits, sm_base_pow + poz);
    m_cif[m_len++] = atoi(buff);
    fix();
  }

  BigInt& operator*=(uint32_t scalar) {
    for (int i = m_len - 1; i >= 0; --i) {      
      uint64_t temp = (uint64_t)m_cif[i] * scalar;
      m_cif[i + 2] += (temp / sm_base) / sm_base;
      m_cif[i + 1] += (temp / sm_base) % sm_base;
      m_cif[i] = temp % sm_base;
    }
    m_len += 2;
    fix();
    return *this;
  }

  BigInt& operator+=(const BigInt& other) {
    for (uint32_t i = 0; i < other.m_len; ++i) {
      m_cif[i] += other.m_cif[i];
    }
    m_len = std::max(m_len, other.m_len);
    fix();
    return *this;
  }

  BigInt& operator+=(int32_t other) {
    m_cif[0] += other;
    fix();
    return *this;
  }

  BigInt operator*(const BigInt& other) {
    BigInt result(0);
    for (uint32_t i = 0; i < m_len; ++i)
      for (uint32_t j = 0; j < other.m_len; ++j) {
	uint64_t temp = ((uint64_t)m_cif[i] * other.m_cif[j]) + result.m_cif[i + j];
	result.m_cif[i + j] = temp % sm_base;
	result.m_cif[i + j + 1] += temp / sm_base;
      }
    result.m_len = m_len + other.m_len;
    result.fix();
    return result;
  }

  BigInt& operator/=(uint32_t div) {
    uint64_t num = 0;
    for (int i = m_len - 1; i >= 0; --i) {
      num *= sm_base;
      num += m_cif[i];
      m_cif[i] = num / div;
      num %= div;
    }
    fix();

    return *this;
  }

  bool operator<(const BigInt& other) { return compare(other) < 0; }
  bool operator<=(const BigInt& other) { return compare(other) <= 0; }

  bool operator>(const BigInt& other) { return compare(other) > 0; }
  bool operator>=(const BigInt& other) { return compare(other) >= 0; }

  bool operator==(const BigInt& other) { return compare(other) == 0; }

  BigInt operator+(const BigInt& other) {
    BigInt result(*this);
    result += other;
    return result;
  }

  BigInt operator+(int32_t other) {
    BigInt result(*this);
    result += other;
    return result;
  }

  void print(std::string& output) {
    fix();
    if (!m_len) { output = "0"; return; }
    
    output.clear();
    add_digit(m_cif[m_len - 1], false, output);
    for (int i = m_len - 2; i >= 0; --i)
      add_digit(m_cif[i], true, output);
  }

  void raw_print() {
    fix();
    
    printf("%lu :", (unsigned long)m_len);
    for (int i = m_len - 1; i >= 0; --i)
      printf(" %lu", (unsigned long)m_cif[i]);
    printf("\n");
  }
  
private:
  void fix() {
    for (int i = 0; i < (signed)m_len; ++i) {
      if (m_cif[i] >= sm_base) {
	m_cif[i + 1] += m_cif[i] / sm_base;
	m_cif[i] %= sm_base;
	if (i + 1 == (signed)m_len) ++m_len;
      }

      if (m_cif[i] < 0) {
	int borrow = (-m_cif[i] + sm_base - 1) / sm_base;
	m_cif[i] += borrow * sm_base;
	m_cif[i + 1] -= borrow;
      }
    }
    while (m_len && m_cif[m_len - 1] == 0) --m_len;
  }

  void add_digit(uint32_t digit, bool force_len, std::string& str) {
    char lol_buffer[128];
    sprintf(lol_buffer, "%%%lu.%lulu", sm_base_pow, sm_base_pow);
    char buffer[128];
    sprintf(buffer, force_len ? lol_buffer : "%lu", (unsigned long)digit);
    str += buffer;
  }

  int compare(const BigInt& other) {
    if (m_len != other.m_len)
      return (m_len < other.m_len) ? -1 : 1;

    for (int i = m_len - 1; i >= 0; --i)
      if (m_cif[i] != other.m_cif[i])
	return (m_cif[i] < other.m_cif[i]) ? -1 : 1;

    return 0;
  }

  static const uint64_t sm_base_pow = 8;
  static const uint64_t sm_base = Pow10<sm_base_pow>::pow;

  uint32_t m_cif[32], m_len;
};


#define FISIN   "numere2.in"
#define FISOUT  "numere2.out"

#define MAXPOW  100

int bad_prime[MAXPOW];

BigInt fastExp(BigInt num, int pow, const BigInt& target, bool& too_big) {
  if (pow == 0) { return BigInt(1); }
  if (pow == 1) { return num; }

  BigInt sqrt = fastExp(num, pow / 2, target, too_big);
  if (too_big) return BigInt(1);

  BigInt temp = sqrt * sqrt;

  /*
  printf("-----------------\n");
  printf("SQRT    = "); sqrt.raw_print();
  printf("TEMP    = "); temp.raw_print();
  printf("-----------------\n");
  */

  BigInt result = (pow & 1) ? (temp * num) : temp;
  if (result > target) {
    too_big = true;
    return BigInt(1);
  }
  return result;
}

void update_en(BigInt& en, const BigInt& target, long pow) {
  BigInt alt = target;
  for (int i = 1; i < pow; ++i)
    alt /= 2;

  if (alt < en) en = alt;
}

int main() {
  FILE *fin = fopen(FISIN, "rt");
  FILE *fout = fopen(FISOUT, "wt");

  char buffer[128];
  fscanf(fin, "%s", buffer);
  BigInt target(buffer);
  int power = 1;

  BigInt en(target);

  for (int i = 2; i < MAXPOW; ++i) {
    if (bad_prime[i]) continue;
    for (int j = i * 2; j < MAXPOW; j += i)
      bad_prime[j] = 1;

    update_en(en, target, i);

    BigInt st(2);
    while (st <= en) {
      BigInt mid = st + en;
      mid /= 2;

      bool too_big = false;
      BigInt pow = fastExp(mid, i, target, too_big);

      /*
      printf("I       = %d\n", i);
      printf("TARGET  = "); target.raw_print();
      printf("ST      = "); st.raw_print();
      printf("EN      = "); en.raw_print(); 
      printf("MID     = "); mid.raw_print(); 
      printf("POW     = "); pow.raw_print(); 
      printf("TOO_BIG = %d\n\n", (int)too_big);
      */

      if (!too_big && pow == target) {
	power *= i;
	target = mid;
	en = target;
	update_en(en, target, i);
	--i;
	break;
      }

      if (too_big || pow > target) {
	en = mid + (-1);
      } else {
	st = mid + 1;
      }
    }
  }

  std::string output; target.print(output);
  
  fprintf(fout, "%s\n%d\n", output.c_str(), power);

  fclose(fout);
  fclose(fin);
  return 0;
}