Codeforces 74 C. Chessboard Billiard
問題
チェスに新たにビリヤード球という駒を導入する。
この駒はビショップのように動くが、盤の端で(何度でも)跳ね返ることができる。
盤の角で跳ね返った場合、元の進行方向へ戻る。
nxmのチェス盤に、互いに効きのないビリヤード球が最大いくつ置けるか求めよ。
制約条件
2≦n,m≦10^6
方針
盤のどの位置に置いたビリヤード球も、一列目に移動することができる。
なので、一列目にいくつビリヤード球が置けるかのみを考えればよい。
一列目をunion-findとして持っておいて、
(y1,0)場所から出発して、一列目に戻ってきたときの座標を(y2,0)としたとき、
y1とy2をマージする。
これを繰り返した後で、一列目がいくつのunionに分かれているかを見ればよい。
ソースコード
int p[1000000]; int root(int x){ if(x==p[x])return x; return p[x]=root(p[x]); } void merge(int a,int b){ a=root(a); b=root(b); if(a!=b)p[a]=b; } int n,m; void run(){ cin>>n>>m; rep(i,n)p[i]=i; rep(i,n)rep(dir,2){ int y=i,x=0,d=dir; do{ int dy,dx; if(d==0)dy=0,dx=m-1; else if(d==1)dy=n-1,dx=m-1; else if(d==2)dy=n-1,dx=0; else if(d==3)dy=0,dx=0; dy=abs(dy-y); dx=abs(dx-x); dy=min(dy,dx); y+=d==0||d==3?-dy:dy; x+=d==2||d==3?-dy:dy; if(y==0||y==n-1)d^=1; if(x==0||x==m-1)d=3-d; }while(x!=0); merge(i,y); } set<int> s; rep(i,n)s.insert(root(i)); cout<<s.size()<<endl; }