roiti46's blog

主に競プロ問題の解説を載せてます

Topcoder SRM671 Div1 Med: BearDurts

SRM671に参加。今回も0完で1321→1281(-32)。

問題

長さN(4 <= N <= 1000)の非負整数からなる数列wを渡される。a=w[i], b=w[j], c=w[k], d=w[l] (i < j < k < l)としたとき、ac = bd となるような選び方は何通りあるか答えよ。

解法

式を変形して a/b = d/c を考える。どこまでをa, bの候補として使うかのループiを回していき、ある既約分数となる値の組み合わせを数え上げていく。各iでc=w[i+1]、d=w[j] (j > i+1)を取り、その既約分数となるa,bの組み合わせがいくつあるかを足しあわせていけば答えとなる。

class BearDarts {
public:
  int gcd(int a, int b) {
    while (b) swap(a %= b, b);
    return a;
  } 

  ll count(vector<int> w) {
    int n = w.size();
    map<pair<int, int>, int> s;
    ll ans = 0;
    for (int i = 1; i < n; i++) {
      for (int j = 0; j < i; j++) {
        int g = gcd(w[i], w[j]);
        s[make_pair(w[j]/g, w[i]/g)]++;
      }
      for (int j = i + 2; j < n; j++) {
        int g = gcd(w[i + 1], w[j]);
        ans += s[make_pair(w[j]/g, w[i + 1]/g)];
      }
    }
    return ans;
  }
};

まとめ

わかってしまえば500ptにしては簡単な問題に見える。

なお同じ部屋でGCDせずにdoubleでキーを作っていた人は誤差で落ちていた。