TopCoder SRM 442 Div1 Medium BedroomFloor
問題
図のような床がある。
(1x5の板が縦に5枚並べた正方形Aと、横に5枚並べた並べた正方形がBが、平面上に
ABABAB
BABABA
ABABAB…
のように無限並んでいる)
この床のx1,y1,x2,y2の長方形の領域を作りたい。
1x5の板はいくつ必要か。
板は好きなように切断することができるが、重ねたり、接合することはできない。
制約条件
x1,y1,x2,y2≦1000000
方針
ひたすら場合分けして、1xXの板がそれぞれ何枚必要かを求める。
それが求まったら、1x5の板からそれらを切り出すには1x5の板が何枚必要かをgreedyに求める。
1x5の板はまるごと買う必要があり、
1x4の板は、1x1の板と出来るだけ組み合わせて買えば良く、
1x3の板はなるべく1x2の板と組み合わせて買ったあと、1x1の板とできるだけ組み合わせて買えばよく、
1x2の板はもう一枚の1x2と1x1とできるだけ組み合わせて買ったあと、1x1三枚と組み合わせて買う。
最後に残った1x1は、5で割って切り上げた枚数だけ1x5を買えば良い。
1xXの板が何枚必要かは、座標が5の倍数の部分と、そうでない部分にわけてそれぞれ求める。
座標が5の倍数の部分とそうでない部分に区切ると対称性から、場合分けが少し減るので楽。
更に、縦の1x5が並んでいる部分から切り出されるか、横の1x5が並んでいる部分から切り出されるかで場合分けする。
ソースコード
vi _(int a,int b){ vi res; res.pb(a); if((a+4)/5*5<=b)res.pb((a+4)/5*5); if(b/5*5>=a)res.pb(b/5*5); res.pb(b); return res; } vector<ll> calc(int x1,int x2,int y1,int y2){ if(x1>=x2||y1>=y2)return vector<ll>(5); int w=x2-x1, h=y2-y1,p=x1/5+y1/5&1; if(x1%5==0&&y1%5==0){ vector<ll> res(5); if(w%5==0&&h%5==0){ res[4]=w/5ll*h; return res; } if(w%5==0){ int yoko=p?w/10:(w/5+1)/2,tate=w/5-yoko; res[h-1]=tate*5ll; res[4]=(ll)yoko*h; return res; } if(h%5==0){ int yoko=p?h/10:(h/5+1)/2,tate=h/5-yoko; res[w-1]=yoko*5ll; res[4]=(ll)tate*w; return res; } if(p)res[h-1]=w; else res[w-1]=h; return res; } else if(x2%5==0&&y1%5==0)return calc(x2+5,x2+w+5,y1,y2); else if(x1%5==0&&y2%5==0)return calc(x1+5,x2+5,y2,y2+h); else if(x2%5==0&&y2%5==0)return calc(x2,x2+w,y2,y2+h); return p?calc(5,5+w,0,h):calc(0,w,0,h); } class BedroomFloor { public: long long numberOfSticks(int x1, int y1, int x2, int y2) { vi x=_(x1,x2), y=_(y1,y2); vector<ll> v(5); rep(i,x.size()-1)rep(j,y.size()-1){ vector<ll> t=calc(x[i],x[i+1],y[j],y[j+1]); rep(k,5)v[k]+=t[k]; } //rep(i,5)cerr<<v[i]<<(i==4?"\n":" "); ll ans=v[4]+v[3]; v[0]=max(0ll,v[0]-v[3]); ans+=v[2]; ll t=max(0ll,v[2]-v[1]); v[1]=max(0ll,v[1]-v[2]); v[0]=max(0ll,v[0]-2*t); ans+=v[1]/2; v[0]=max(0ll,v[0]-v[1]/2); ans+=v[1]%2; v[0]=max(0ll,v[0]-v[1]%2*3); ans+=(v[0]+4)/5; return ans; } };