SRM 458 Div2 Hard ProductTriplet
minx≦x≦maxx,miny≦y≦maxy,minz≦z≦maxzを満たす整数の組
(x,y,z)の個数を求めよ。
ただし1≦minx,miny,minz,maxx,maxy,maxz≦1,000,000,000である。
数字が10億と大きいのでナイーブにループを回して数え上げる方法は取れない。
なんとか√10億(≒3万)くらいのループで数える方法を考える必要がある。
するとx≦yとなる組だけ数えてみるという発想に至る。
xを固定したとき、xy=zよりminz/x≦y≦maxz/xであるので、
条件を満たすようなyは
上限A=min(maxz/x,maxy),下限B=max(minz/x,miny)の間にあり
かつy≧xであるような整数である。
x≦yを仮定したので最悪でもxは1から3万まで調べればよい。
次にy≦xの場合を同様に数え、最後に2回数えたx=yの場合を引く。
class ProductTriplet { public: long long countTriplets(int minx, int maxx, int miny, int maxy, int minz, int maxz) { ll cnt=0; //x<=y cnt+=calc(minx,maxx,miny,maxy,minz,maxz); //x>=y cnt+=calc(miny,maxy,minx,maxx,minz,maxz); //x=yを二回数えたので一回引く int A=min(maxx,maxy),B=max(minx,miny); A=min(A,(int)sqrt(maxz)); B=max(B,(int)ceil(sqrt(double(minz)))); if(A>=B)cnt-=A-B+1; return cnt; } ll calc(int mx,int Mx,int my,int My,int mz,int Mz) { ll ret=0; int X=(int)sqrt(Mz);X=min(X,Mx); //x<=yのものを数えるので√Mzまで考えれば十分 for(int x=mx;x<=X;x++) { int A=Mz/x; //今のxに対するyの上限 int B=(int)ceil(double(mz)/x); //y下限 A=min(A,My); B=max(B,max(x,my)); //yの範囲に注意(miny,maxyの条件とx<=yの条件) if(A>=B)ret+=A-B+1; //yが存在したときだけ足す } return ret; } }
(ヘッダ、マクロは省略。llはlong longにtypedefされている)
条件が3つかぶる&下限は切り上げる必要がある等、本番ではまりうる細かい落とし穴が多いかも?