SRM 473 500pt: RightTriangle
問題概要
円周上に等間隔にN(<10^6)個の点がばらまかれる。このうちP(<10^5)個を赤く塗る。赤くする点の選び方は、i=0,1,..,P-1の順に、X[i]番目から順に見ていって初めて赤くない点を赤くする。全ての頂点が赤い直角三角形の個数を求める問題。
考えたこと
practiceで通ったコード
計算量O(N*log N)。
#include <set> #include <vector> #include <algorithm> using namespace std; typedef long long int64; const int MAX_N = 1000000; bool is_red[MAX_N]; struct RightTriangle { int64 triangleCount(int places, int points, int64 a, int64 b, int64 c) { //奇数は無理 if(places&1){ return 0; } //色塗りパート set<int> whites; for(int i=0; i<places; i++){ whites.insert(i); } for(int i=0; i<points; i++){ int pn = (a * i * i + b * i + c) % places; set<int>::iterator itr = whites.lower_bound(pn); if( itr != whites.end() ){ is_red[*itr] = true; whites.erase(itr); } else{ is_red[*whites.begin()] = true; whites.erase(whites.begin()); } } //二分探索用に赤を集める vector<int> reds; for(int i=0; i<places; i++){ if(is_red[i]){ reds.push_back(i); reds.push_back(i + places); } } sort(reds.begin(), reds.end()); //直角三角形探しパート int64 ret = 0; for(int i=0; i<places; i++)if(is_red[i] && is_red[(i+places/2)%places]){ vector<int>::iterator lb = lower_bound(reds.begin(), reds.end(), i+1), ub = upper_bound(reds.begin(), reds.end(), i+places/2-1); ret += distance(lb, ub); } return ret; } };
他の人の解放
- 直角三角形の数を数える部分では、二分探索ではなくて累積和を利用している人がいた。確かにそっちの方が速い(全体の計算量は変わらないが)。