Problem p02679 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>

#include <bits/stdc++.h>
#include <cmath>
#include <limits>

using namespace std;

#define MAX 200002
long long MOD = 1000000007;
long long MODPOW2[MAX] = {0};

void initModPow2() {
  MODPOW2[0] = 1;
  for (int i = 1; i < MAX; i++) {
    MODPOW2[i] = (MODPOW2[i - 1] * 2) % MOD;
  }
}

long long GCD(long long x, long long y) {
  long long r = 0;

  if (x < y)
    swap(x, y);
  r = x % y;
  while (r != 0) {
    x = y;
    y = r;
    r = x % y;
  }
  return y;
}

// U(u個)とV(v個)が直交しているとき、どの2つも内積が0にならない選び方の数
// 余事象で考えて、2**(u+v)から内積が0になりうる選び方の数を引く
long long countUV(long long u, long long v) {
  long long a = 0, b = 0, c = 0, ret = 0;
  a = MODPOW2[u + v] % MOD;
  b = (MODPOW2[u] - 1) % MOD;
  b = (b * (MODPOW2[v] - 1)) % MOD;
  ret = (a - b) % MOD;
  if (ret < 0)
    ret += MOD;
  return ret;
}

class Tilt {
public:
  long long x;
  long long y;
  Tilt() { x = 0, y = 0; }
  Tilt(long long x, long long y) {
    if (x == 0) {
      this->x = 0;
      this->y = 1;
    } else if (y == 0) {
      this->x = 1;
      this->y = 0;
    } else {
      long p, a, b;
      p = GCD(x, y);
      a = x / p;
      b = y / p;
      // if(a > 0){
      if (b > 0) {
        this->x = a;
        this->y = b;
      } else {
        this->x = 0 - a;
        this->y = 0 - b;
      }
    }
  }
  inline bool operator==(const Tilt &t) const {
    if (x == 0 && t.x != 0)
      return false;
    if (x != 0 && t.x == 0)
      return false;
    if (x == 0 && t.x == 0)
      return true;
    if (y == 0 && t.y != 0)
      return false;
    if (y != 0 && t.y == 0)
      return false;
    if (y == 0 && t.y == 0)
      return true;

    if (x == t.x && y == t.y) {
      return true;
    } else {
      return false;
    }
  }
  inline bool operator!=(const Tilt &t) const { return !(this->operator==(t)); }
};

// ハッシュ関数定義
// 約分されている前提で、分子の値を返す
class MyHash {
public:
  std::size_t operator()(const Tilt &t) const { return (std::size_t)abs(t.y); }
};

unordered_map<Tilt, long long, MyHash> m1;
unordered_map<Tilt, long long, MyHash> m2; // ワーク用

int main(int argc, char *argv[]) {
  int N, M;
  long long zero = 0;

  cin >> N;
  m1.reserve(N);
  m2.reserve(N);

  initModPow2();
  long long ans = 1;
  for (int i = 1; i <= N; i++) {
    long long a, b;
    cin >> a >> b;

    if (a == 0 && b == 0) {
      zero++;
      continue; //(0, 0)は除外するが、数えておく
    }
    Tilt t = Tilt(a, b);
    if (m1.count(t) == 0) {
      m1[t] = 1;
      m2[t] = 1;
    } else {
      long long v = m1[t];
      m1[t] = v + 1;
      m2[t] = v + 1;
    }
  }
  for (auto itr = m1.begin(); itr != m1.end(); itr++) {
    if (m2.empty())
      break;
    long long x, y, u, v;
    Tilt t = itr->first;
    auto tmpitr1 = m2.find(t);
    if (tmpitr1 == m2.end()) {
      continue;
    }
    u = itr->second;
    x = t.x;
    y = t.y;
    Tilt w = Tilt(0 - y, x); // 法線ベクトル
    auto tmpitr2 = m2.find(w);
    if (tmpitr2 != m2.end()) {
      v = m1[w];
    } else {
      v = 0;
    }
    long long uvcnt = countUV(u, v);
    ans = (ans * uvcnt) % MOD;
    m2.erase(t);
    m2.erase(w);
  }
  ans = ans + (zero % MOD) -
        1; //(0, 0)は他のどの要素とも直交するので足す。まったく選ばないを引く。
  ans = ans % MOD;
  if (ans < 0)
    ans += MOD;
  cout << ans << endl;
  return 0;
}

Error: TLE

Fixed submission:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>

#include <bits/stdc++.h>
#include <cmath>
#include <limits>

using namespace std;

#define MAX 200002
long long MOD = 1000000007;
long long MODPOW2[MAX] = {0};

void initModPow2() {
  MODPOW2[0] = 1;
  for (int i = 1; i < MAX; i++) {
    MODPOW2[i] = (MODPOW2[i - 1] * 2) % MOD;
  }
}

// U(u個)とV(v個)が直交しているとき、どの2つも内積が0にならない選び方の数
// 余事象で考えて、2**(u+v)から内積が0になりうる選び方の数を引く
long long countUV(long long u, long long v) {
  long long a = 0, b = 0, c = 0, ret = 0;
  a = MODPOW2[u + v] % MOD;
  b = (MODPOW2[u] - 1) % MOD;
  b = (b * (MODPOW2[v] - 1)) % MOD;
  ret = (a - b) % MOD;
  if (ret < 0)
    ret += MOD;
  return ret;
}

class Tilt {
public:
  long long x;
  long long y;
  Tilt(long long x, long long y) { this->x = x, this->y = y; }
  inline bool operator==(const Tilt &t) const {
    if (this->x * t.y - this->y * t.x == 0) {
      return true;
    } else {
      return false;
    }
  }
  inline bool operator!=(const Tilt &t) const { return !(this->operator==(t)); }
};

// ハッシュ関数定義
class MyHash {
public:
  std::size_t operator()(const Tilt &t) const {
    if (t.y == 0) {
      return (std::size_t)MOD;
    } else {
      long long r = t.x / t.y;
      r = r % MOD;
      return (std::size_t)r;
    }
  }
};

vector<Tilt> T;
unordered_map<Tilt, long long, MyHash> mt;
unordered_map<Tilt, long long, MyHash> mw; // ワーク用

int main(int argc, char *argv[]) {
  int N, M;
  long long zero = 0;

  cin >> N;

  initModPow2();

  long long ans = 1;
  for (int i = 1; i <= N; i++) {
    long long a, b;
    cin >> a >> b;
    if (a == 0 && b == 0) {
      zero++;
      continue; //(0, 0)は除外するが、数えておく
    }
    Tilt t = Tilt(a, b);
    if (mt.count(t) == 0) {
      mt[t] = 1;
      mw[t] = 1;
    } else {
      long long v = mt[t];
      mt[t] = v + 1;
      mw[t] = v + 1;
    }
  }
  for (auto itr = mt.begin(); itr != mt.end(); ++itr) {
    if (mw.empty())
      break;
    long long x, y, u, v;
    Tilt t = itr->first;
    auto tmpitr1 = mw.find(t);
    if (tmpitr1 == mw.end()) {
      continue;
    }
    u = itr->second;
    x = t.x;
    y = t.y;
    Tilt w = Tilt((-1) * y, x);
    auto tmpitr2 = mw.find(w);
    if (tmpitr2 != mw.end()) {
      v = mt[w];
    } else {
      v = 0;
    }
    long long uvcnt = countUV(u, v);
    ans = (ans * uvcnt) % MOD;
    mw.erase(t);
    mw.erase(w);
  }
  ans = ans + (zero % MOD) -
        1; //(0, 0)は他のどの要素とも直交するので足す。まったく選ばないを引く。
  ans = ans % MOD;
  if (ans < 0)
    ans += MOD;
  cout << ans << endl;
  return 0;
}