def parse(inFile):
[H,W,D] = inFile.getInts()
return (H,W,D,[inFile.readline() for k in xrange(H)])
from fractions import Fraction, gcd
def cansee((H,W),(x,y),(dx,dy),D,grid):
if (dy < 0):
return cansee((H,W),(x,(W - 1 - y)),(dx,-dy),D,[z[::-1] for z in grid])
if (dx < 0):
return cansee((H,W),((H - 1 - x),y),(-dx,dy),D,grid[::-1])
if (dx == 0) and (dy == 1):
z = y + 1
while grid[x][z] == ".":
z = z + 1
return (2 * (z - y) - 1) <= D
if (dy == 0) and (dx == 1):
z = x + 1
while grid[z][y] == ".":
z = z + 1
return (2 * (z - x) - 1) <= D
kk = 1
while ((kk + 1) * (kk + 1)) * (dx * dx + dy * dy) <= D * D:
kk += 1
horizontalIntersections = [[Fraction(2*k-1,2*dx),0] for k in xrange(1,dx+1)]
verticalIntersections = [[Fraction(2*k-1,2*dy),1] for k in xrange(1,dy+1)]
if (dx & dy & 1):
half = Fraction(1,2)
ints = [z for z in horizontalIntersections if z[0] != half] + [z for z in verticalIntersections if z[0] != half] + [[half,2]]
else:
ints = horizontalIntersections+verticalIntersections
if True:
ints.sort()
posn = (Fraction(2*x+1,2),Fraction(2*y+1,2))
dirn = (dx, dy)
timeAt = 0
for m in xrange(kk):
for isn in ints:
dt = isn[0] - timeAt
timeAt = isn[0]
posn = (posn[0] + dirn[0] * dt, posn[1] + dirn[1] * dt)
(xat,yat) = (int(posn[0]),int(posn[1]))
if (isn[1] == 0):
if (dirn[0] > 0) and grid[xat][yat] == "#":
dirn = (-dirn[0],dirn[1])
elif (dirn[0] < 0) and grid[xat-1][yat] == "#":
dirn = (-dirn[0],dirn[1])
elif (isn[1] == 1):
if (dirn[1] > 0) and grid[xat][yat] == "#":
dirn = (dirn[0],-dirn[1])
elif (dirn[1] < 0) and grid[xat][yat-1] == "#":
dirn = (dirn[0],-dirn[1])
elif (isn[1] == 2):
nowx = xat if (dirn[0] < 0) else (xat - 1)
nextx = (xat - 1) if (dirn[0] < 0) else xat
nowy = yat if (dirn[1] < 0) else (yat - 1)
nexty = (yat - 1) if (dirn[1] < 0) else yat
up = grid[nextx][nowy] == "#"
right = grid[nowx][nexty] == "#"
ur = grid[nextx][nexty] == "#"
assert(grid[nowx][nowy] != "#")
if ur:
if up:
if right:
dirn = (-dirn[0], -dirn[1])
else:
dirn = (-dirn[0], dirn[1])
else:
if right:
dirn = (dirn[0], -dirn[1])
else:
return False
dt = 1 - timeAt
timeAt = 0
posn = (posn[0] + dirn[0] * dt, posn[1] + dirn[1] * dt)
if (posn == (Fraction(2*x+1,2),Fraction(2*y+1,2))):
return True
return False
def solve((H,W,D,grid)):
count = 0
(x,y) = [(i,j) for i in xrange(H) for j in xrange(W) if grid[i][j] == "X"][0]
for dx in xrange(-D,D+1):
for dy in xrange(-D,D+1):
if (dx * dx + dy * dy) <= D * D and abs(gcd(dx,dy)) == 1:
if cansee((H,W),(x,y),(dx,dy),D,grid):
count += 1
return count
if __name__ == "__main__":
from GCJ import GCJ
GCJ(parse, solve, "/Users/lpebody/gcj/2012_q/", "d").run()
ZGVmIHBhcnNlKGluRmlsZSk6CiAgICBbSCxXLERdID0gaW5GaWxlLmdldEludHMoKQogICAgcmV0dXJuIChILFcsRCxbaW5GaWxlLnJlYWRsaW5lKCkgZm9yIGsgaW4geHJhbmdlKEgpXSkKCmZyb20gZnJhY3Rpb25zIGltcG9ydCBGcmFjdGlvbiwgZ2NkCgpkZWYgY2Fuc2VlKChILFcpLCh4LHkpLChkeCxkeSksRCxncmlkKToKICAgIGlmIChkeSA8IDApOgogICAgICAgIHJldHVybiBjYW5zZWUoKEgsVyksKHgsKFcgLSAxIC0geSkpLChkeCwtZHkpLEQsW3pbOjotMV0gZm9yIHogaW4gZ3JpZF0pCiAgICBpZiAoZHggPCAwKToKICAgICAgICByZXR1cm4gY2Fuc2VlKChILFcpLCgoSCAtIDEgLSB4KSx5KSwoLWR4LGR5KSxELGdyaWRbOjotMV0pCiAgICBpZiAoZHggPT0gMCkgYW5kIChkeSA9PSAxKToKICAgICAgICB6ID0geSArIDEKICAgICAgICB3aGlsZSBncmlkW3hdW3pdID09ICIuIjoKICAgICAgICAgICAgeiA9IHogKyAxCiAgICAgICAgcmV0dXJuICgyICogKHogLSB5KSAtIDEpIDw9IEQKICAgIGlmIChkeSA9PSAwKSBhbmQgKGR4ID09IDEpOgogICAgICAgIHogPSB4ICsgMQogICAgICAgIHdoaWxlIGdyaWRbel1beV0gPT0gIi4iOgogICAgICAgICAgICB6ID0geiArIDEKICAgICAgICByZXR1cm4gKDIgKiAoeiAtIHgpIC0gMSkgPD0gRAogICAga2sgPSAxCiAgICB3aGlsZSAoKGtrICsgMSkgKiAoa2sgKyAxKSkgKiAoZHggKiBkeCArIGR5ICogZHkpIDw9IEQgKiBEOgogICAgICAgIGtrICs9IDEKICAgIGhvcml6b250YWxJbnRlcnNlY3Rpb25zID0gW1tGcmFjdGlvbigyKmstMSwyKmR4KSwwXSBmb3IgayBpbiB4cmFuZ2UoMSxkeCsxKV0KICAgIHZlcnRpY2FsSW50ZXJzZWN0aW9ucyA9IFtbRnJhY3Rpb24oMiprLTEsMipkeSksMV0gZm9yIGsgaW4geHJhbmdlKDEsZHkrMSldCiAgICBpZiAoZHggJiBkeSAmIDEpOgogICAgICAgIGhhbGYgPSBGcmFjdGlvbigxLDIpCiAgICAgICAgaW50cyA9IFt6IGZvciB6IGluIGhvcml6b250YWxJbnRlcnNlY3Rpb25zIGlmIHpbMF0gIT0gaGFsZl0gKyBbeiBmb3IgeiBpbiB2ZXJ0aWNhbEludGVyc2VjdGlvbnMgaWYgelswXSAhPSBoYWxmXSArIFtbaGFsZiwyXV0KICAgIGVsc2U6CiAgICAgICAgaW50cyA9IGhvcml6b250YWxJbnRlcnNlY3Rpb25zK3ZlcnRpY2FsSW50ZXJzZWN0aW9ucwogICAgaWYgVHJ1ZToKICAgICAgICBpbnRzLnNvcnQoKQogICAgICAgIHBvc24gPSAoRnJhY3Rpb24oMip4KzEsMiksRnJhY3Rpb24oMip5KzEsMikpCiAgICAgICAgZGlybiA9IChkeCwgZHkpCiAgICAgICAgdGltZUF0ID0gMAogICAgICAgIGZvciBtIGluIHhyYW5nZShrayk6CiAgICAgICAgICAgIGZvciBpc24gaW4gaW50czoKICAgICAgICAgICAgICAgIGR0ID0gaXNuWzBdIC0gdGltZUF0CiAgICAgICAgICAgICAgICB0aW1lQXQgPSBpc25bMF0KICAgICAgICAgICAgICAgIHBvc24gPSAocG9zblswXSArIGRpcm5bMF0gKiBkdCwgcG9zblsxXSArIGRpcm5bMV0gKiBkdCkKICAgICAgICAgICAgICAgICh4YXQseWF0KSA9IChpbnQocG9zblswXSksaW50KHBvc25bMV0pKQogICAgICAgICAgICAgICAgaWYgKGlzblsxXSA9PSAwKToKICAgICAgICAgICAgICAgICAgICBpZiAoZGlyblswXSA+IDApIGFuZCBncmlkW3hhdF1beWF0XSA9PSAiIyI6CiAgICAgICAgICAgICAgICAgICAgICAgIGRpcm4gPSAoLWRpcm5bMF0sZGlyblsxXSkKICAgICAgICAgICAgICAgICAgICBlbGlmIChkaXJuWzBdIDwgMCkgYW5kIGdyaWRbeGF0LTFdW3lhdF0gPT0gIiMiOgogICAgICAgICAgICAgICAgICAgICAgICBkaXJuID0gKC1kaXJuWzBdLGRpcm5bMV0pCiAgICAgICAgICAgICAgICBlbGlmIChpc25bMV0gPT0gMSk6CiAgICAgICAgICAgICAgICAgICAgaWYgKGRpcm5bMV0gPiAwKSBhbmQgZ3JpZFt4YXRdW3lhdF0gPT0gIiMiOgogICAgICAgICAgICAgICAgICAgICAgICBkaXJuID0gKGRpcm5bMF0sLWRpcm5bMV0pCiAgICAgICAgICAgICAgICAgICAgZWxpZiAoZGlyblsxXSA8IDApIGFuZCBncmlkW3hhdF1beWF0LTFdID09ICIjIjoKICAgICAgICAgICAgICAgICAgICAgICAgZGlybiA9IChkaXJuWzBdLC1kaXJuWzFdKQogICAgICAgICAgICAgICAgZWxpZiAoaXNuWzFdID09IDIpOgogICAgICAgICAgICAgICAgICAgIG5vd3ggPSAgeGF0IGlmIChkaXJuWzBdIDwgMCkgZWxzZSAoeGF0IC0gMSkKICAgICAgICAgICAgICAgICAgICBuZXh0eCA9ICh4YXQgLSAxKSBpZiAoZGlyblswXSA8IDApIGVsc2UgeGF0CiAgICAgICAgICAgICAgICAgICAgbm93eSA9ICB5YXQgaWYgKGRpcm5bMV0gPCAwKSBlbHNlICh5YXQgLSAxKQogICAgICAgICAgICAgICAgICAgIG5leHR5ID0gKHlhdCAtIDEpIGlmIChkaXJuWzFdIDwgMCkgZWxzZSB5YXQKICAgICAgICAgICAgICAgICAgICB1cCA9IGdyaWRbbmV4dHhdW25vd3ldID09ICIjIgogICAgICAgICAgICAgICAgICAgIHJpZ2h0ID0gZ3JpZFtub3d4XVtuZXh0eV0gPT0gIiMiCiAgICAgICAgICAgICAgICAgICAgdXIgPSBncmlkW25leHR4XVtuZXh0eV0gPT0gIiMiCiAgICAgICAgICAgICAgICAgICAgYXNzZXJ0KGdyaWRbbm93eF1bbm93eV0gIT0gIiMiKQogICAgICAgICAgICAgICAgICAgIGlmIHVyOgogICAgICAgICAgICAgICAgICAgICAgICBpZiB1cDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIHJpZ2h0OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcm4gPSAoLWRpcm5bMF0sIC1kaXJuWzFdKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJuID0gKC1kaXJuWzBdLCBkaXJuWzFdKQogICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgcmlnaHQ6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlybiA9IChkaXJuWzBdLCAtZGlyblsxXSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIEZhbHNlCiAgICAgICAgICAgIGR0ID0gMSAtIHRpbWVBdAogICAgICAgICAgICB0aW1lQXQgPSAwCiAgICAgICAgICAgIHBvc24gPSAocG9zblswXSArIGRpcm5bMF0gKiBkdCwgcG9zblsxXSArIGRpcm5bMV0gKiBkdCkKICAgICAgICAgICAgaWYgKHBvc24gPT0gKEZyYWN0aW9uKDIqeCsxLDIpLEZyYWN0aW9uKDIqeSsxLDIpKSk6CiAgICAgICAgICAgICAgICByZXR1cm4gVHJ1ZQogICAgcmV0dXJuIEZhbHNlCgpkZWYgc29sdmUoKEgsVyxELGdyaWQpKToKICAgIGNvdW50ID0gMAogICAgKHgseSkgPSBbKGksaikgZm9yIGkgaW4geHJhbmdlKEgpIGZvciBqIGluIHhyYW5nZShXKSBpZiBncmlkW2ldW2pdID09ICJYIl1bMF0KICAgIGZvciBkeCBpbiB4cmFuZ2UoLUQsRCsxKToKICAgICAgICBmb3IgZHkgaW4geHJhbmdlKC1ELEQrMSk6CiAgICAgICAgICAgIGlmIChkeCAqIGR4ICsgZHkgKiBkeSkgPD0gRCAqIEQgYW5kIGFicyhnY2QoZHgsZHkpKSA9PSAxOgogICAgICAgICAgICAgICAgaWYgY2Fuc2VlKChILFcpLCh4LHkpLChkeCxkeSksRCxncmlkKToKICAgICAgICAgICAgICAgICAgICBjb3VudCArPSAxCiAgICByZXR1cm4gY291bnQKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBmcm9tIEdDSiBpbXBvcnQgR0NKCiAgICBHQ0oocGFyc2UsIHNvbHZlLCAiL1VzZXJzL2xwZWJvZHkvZ2NqLzIwMTJfcS8iLCAiZCIpLnJ1bigpCgogICAgICAgICAgICAK