google code jam 2010 Round 1C Rope Intranet

問題

http://code.google.com/codejam/contest/dashboard?c=619102#s=p0

  • ビルが2つ並んでいて、左のビルの高さA[i]の所から右のビルの高さB[i]の所に電線が張ってある。
  • 電線の交点の数を求めなさい、という問題。
  • 但し、3つの線が一点で交わる事はない事は保証されている。

方針

  • 素直に数える。
  • i番目の線とj番目の線が交わるかどうかは、A[i] - A[j]とB[i] - B[j]が同符号か否かで判定。
    • (A[i] - A[j] > 0) と (B[i] - B[j] > 0)のXORでも良い。
  • 今回は、サイズが小さいので数え上げで問題なかったけど、サイズが大きい場合、Binary Indexed Treeで解けるらしい。
    • セグメント木とBITについては以前も勉強したのに、全然、そんな解法思い付かなんだ。泣
    • この解法について解説してくれたのは、弱冠二十歳でプログラミング歴3年くらいの若者でした。凄。

回答例

/*! g++ main.cpp -DNDEBUG
 */

#include <vector>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;
typedef unsigned long ul;

void solve(int t){
  int n;
  cin >> n;

  vector<int> a(n), b(n);
  for(int i = 0; i < n; i++){
    cin >> a[i] >> b[i];
  }

  int ret = 0;
  for(int i = 0; i < n; i++){
    for(int j = 0; j < i; j++){
      ret += (a[i] - a[j]) * (b[i] - b[j]) < 0 ? 1 : 0;
    }
  }

  cout << "Case #" << t << ": " << ret << endl;
}

int main(int argc, char* argv){

  int num_case;
  cin >> num_case;

  for(int i = 0; i < num_case; i++){
    solve(i + 1);
  }

  return 0;
}