Problem p04022 (C++)

Buggy submission (status: Time Limit Exceeded):
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unordered_map>
#include <utility>
#define FR first
#define SE second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ull, ull> pr;

inline ll mul(ll x, ll y, ll MOD) {
  x %= MOD;
  y %= MOD;
  ll ans = 0;
  while (y) {
    if (y & 1)
      ans = (ans + x) % MOD;
    x = (x + x) % MOD;
    y >>= 1;
  }
  return ans;
}

ll gcd(ll x, ll y) { return (x % y == 0) ? y : gcd(y, x % y); }

ll pow_mod(ll x, ll k, ll MOD) {
  ll ans = 1;
  while (k) {
    if (k & 1)
      ans = mul(ans, x, MOD);
    x = mul(x, x, MOD);
    k >>= 1;
  }
  return ans;
}

bool is_prime(int c, ll n) {
  ll d = n - 1;
  while (!(d & 1))
    d >>= 1;
  ll t = pow_mod(c, d, n);
  while (d != n - 1 && t != n - 1 && t != 1) {
    t = mul(t, t, n);
    d <<= 1;
  }
  return (t == n - 1) || (d & 1);
}

const int pri[3] = {2, 7, 61};

bool miller_rabin(ll n) {
  if (n == 4759123141LL)
    return 0;
  for (int i = 0; i < 2; i++)
    if (!is_prime(pri[i], n))
      return 0;
  return 1;
}

int prime[3005], tot;
bool check[3005];

void pre() {
  int N = pow(1e10, 1.0 / 3);
  for (int i = 2; i <= N; i++) {
    if (!check[i])
      prime[++tot] = i;
    for (int j = 1; j <= tot && i * prime[j] <= N; j++) {
      check[i * prime[j]] = 1;
      if (i % prime[j] == 0)
        break;
    }
  }
}

unordered_map<ll, ull> mp;

ull rd() { return (ull)rand() * rand() * 1063094364578754ull + rand(); }

ull getrd(ll x) {
  if (!mp.count(x))
    mp[x] = rd();
  return mp[x];
}

pr now[15];

pr calc(int n) {
  ull s1 = 0, s2 = 0;
  for (int i = 1; i <= n; i++)
    if (now[i].SE % 3) {
      now[i].SE %= 3;
      s1 ^= getrd(now[i].FR * now[i].SE);
      s2 ^= getrd(now[i].FR * (3 - now[i].SE));
    }
  return pr(s1, s2);
}

pr a[100005];
ull num[100005];
int sum[100005];
bool vis[100005];

int main() {
  srand(19260817);
  pre();
  int n;
  scanf("%d", &n);
  int m = 0, s1 = 0, s2 = 0;
  for (int i = 1; i <= n; i++) {
    ll x;
    scanf("%lld", &x);
    int cnt = 0;
    for (int j = 1; j <= tot && prime[j] <= x; j++)
      if (x % prime[j] == 0) {
        now[++cnt] = pr(prime[j], 0);
        while (x % prime[j] == 0) {
          now[cnt].SE++;
          x /= prime[j];
        }
      }
    if (x > 1) {
      ll t = sqrt(x);
      while (t * t > x)
        t--;
      while (t * t < x)
        t++;
      if (t * t == x)
        now[++cnt] = pr(t, 2);
      else if (miller_rabin(x))
        now[++cnt] = pr(x, 1);
      else {
        s1++;
        continue;
      }
    }
    pr t = calc(cnt);
    if (t.FR) {
      a[++m] = t;
      num[m] = t.FR;
    } else
      s2 = 1;
  }
  sort(num + 1, num + m + 1);
  for (int i = 1; i <= m; i++) {
    a[i].FR = lower_bound(num + 1, num + m + 1, a[i].FR) - num;
    sum[a[i].FR]++;
    ull t = a[i].SE;
    a[i].SE = lower_bound(num + 1, num + m + 1, a[i].SE) - num;
    if (num[a[i].SE] != t)
      a[i].SE = 0;
  }
  int ans = 0;
  for (int i = 1; i <= m; i++)
    if (!vis[a[i].FR]) {
      ans += max(sum[a[i].FR], sum[a[i].SE]);
      vis[a[i].FR] = vis[a[i].SE] = 1;
    }
  printf("%d\n", ans + s1 + s2);
  return 0;
}

Fixed submission:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unordered_map>
#include <utility>
#define FR first
#define SE second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ull, ull> pr;

inline ll mul(ll x, ll y, ll MOD) {
  ll t = (x * y - (ll)((long double)x / MOD * y + 1e-8) * MOD);
  return (t < 0) ? t + MOD : t;
}

ll gcd(ll x, ll y) { return (x % y == 0) ? y : gcd(y, x % y); }

ll pow_mod(ll x, ll k, ll MOD) {
  ll ans = 1;
  while (k) {
    if (k & 1)
      ans = mul(ans, x, MOD);
    x = mul(x, x, MOD);
    k >>= 1;
  }
  return ans;
}

bool is_prime(int c, ll n) {
  ll d = n - 1;
  while (!(d & 1))
    d >>= 1;
  ll t = pow_mod(c, d, n);
  while (d != n - 1 && t != n - 1 && t != 1) {
    t = mul(t, t, n);
    d <<= 1;
  }
  return (t == n - 1) || (d & 1);
}

const int pri[3] = {2, 7, 61};

bool miller_rabin(ll n) {
  if (n == 4759123141LL)
    return 0;
  for (int i = 0; i < 2; i++)
    if (!is_prime(pri[i], n))
      return 0;
  return 1;
}

int prime[3005], tot;
bool check[3005];

void pre() {
  int N = pow(1e10, 1.0 / 3);
  for (int i = 2; i <= N; i++) {
    if (!check[i])
      prime[++tot] = i;
    for (int j = 1; j <= tot && i * prime[j] <= N; j++) {
      check[i * prime[j]] = 1;
      if (i % prime[j] == 0)
        break;
    }
  }
}

unordered_map<ll, ull> mp;

ull rd() { return (ull)rand() * rand() * 1063094364578754ull + rand(); }

ull getrd(ll x) {
  if (!mp.count(x))
    mp[x] = rd();
  return mp[x];
}

pr now[15];

pr calc(int n) {
  ull s1 = 0, s2 = 0;
  for (int i = 1; i <= n; i++)
    if (now[i].SE % 3) {
      now[i].SE %= 3;
      s1 ^= getrd(now[i].FR * now[i].SE);
      s2 ^= getrd(now[i].FR * (3 - now[i].SE));
    }
  return pr(s1, s2);
}

pr a[100005];
ull num[100005];
int sum[100005];
bool vis[100005];

int main() {
  srand(19260817);
  pre();
  int n;
  scanf("%d", &n);
  int m = 0, s1 = 0, s2 = 0;
  for (int i = 1; i <= n; i++) {
    ll x;
    scanf("%lld", &x);
    int cnt = 0;
    for (int j = 1; j <= tot && prime[j] <= x; j++)
      if (x % prime[j] == 0) {
        now[++cnt] = pr(prime[j], 0);
        while (x % prime[j] == 0) {
          now[cnt].SE++;
          x /= prime[j];
        }
      }
    if (x > 1) {
      ll t = sqrt(x);
      while (t * t > x)
        t--;
      while (t * t < x)
        t++;
      if (t * t == x)
        now[++cnt] = pr(t, 2);
      else if (miller_rabin(x))
        now[++cnt] = pr(x, 1);
      else {
        s1++;
        continue;
      }
    }
    pr t = calc(cnt);
    if (t.FR) {
      a[++m] = t;
      num[m] = t.FR;
    } else
      s2 = 1;
  }
  sort(num + 1, num + m + 1);
  for (int i = 1; i <= m; i++) {
    a[i].FR = lower_bound(num + 1, num + m + 1, a[i].FR) - num;
    sum[a[i].FR]++;
    ull t = a[i].SE;
    a[i].SE = lower_bound(num + 1, num + m + 1, a[i].SE) - num;
    if (num[a[i].SE] != t)
      a[i].SE = 0;
  }
  int ans = 0;
  for (int i = 1; i <= m; i++)
    if (!vis[a[i].FR]) {
      ans += max(sum[a[i].FR], sum[a[i].SE]);
      vis[a[i].FR] = vis[a[i].SE] = 1;
    }
  printf("%d\n", ans + s1 + s2);
  return 0;
}