TopCoder SRM 473 Div 1 Medium RightTriangle
本番でテスト落ちたやつ。
問題概要
円周上をn等分する点に、時計回りに0,1,2,...,n-1と番号をつける。
そして、0,1,2,...,m-1のそれぞれのkに対して、
l=(a*a*k+b*k+c)%nとし、lから数えて初めて最初に赤でない点を赤にする。
この操作を終えたとき、円周上の異なる3つの赤い点を結んでできる直角三角形はいくつできるかを求めよ。
n≦1000000, 赤い点の数mはm≦100000を満たす。
解法
赤い点を作るのにまだ赤くなっていない点を探す必要があるが、これを線形探索で行うと明らかにTLE.
なので平方分割した。√nの大きさの区間にnを区切り、それぞれに赤い点を何個作ったかを数えておく。
ソースコード
int P,sqp,last; int cnt[1000]; bool red[1000000]; void ins(ll n) { int m,lim; while(1) { m=n/sqp; while(cnt[m]==sqp||m==sqp-1&&cnt[m]==last) { m++; n=m*sqp; if(n>=P)n=m=0; } lim=min(sqp*(m+1),P); while(n<lim&&red[n])n++; if(n<lim) { red[n]=1; cnt[m]++; return; } if(n==P)n=0; } } class RightTriangle { public: long long triangleCount(int places, int points, int A, int B, int C) { P=places; sqp=(int)ceil(sqrt(P*1.0)); last=P%sqp; if(last==0)last=sqp; dbg(P);dbg(sqp);dbg(last); if(P%2)return 0; fill_n(cnt,1000,0); fill_n(red,P,0); ll a=A,b=B,c=C; rep(i,points) { ll n=(a*i*i+b*i+c)%P; ins(n); } ll ans=0; rep(i,P/2)if(red[i]) { if(red[i+P/2]) ans+=points-2; } return ans; } };