SHOW:
|
|
- or go back to the newest paste.
| 1 | import multiprocessing | |
| 2 | import cPickle | |
| 3 | import time | |
| 4 | import bisect | |
| 5 | import math | |
| 6 | from files import * | |
| 7 | ||
| 8 | """ | |
| 9 | Things yet to do: | |
| 10 | ||
| 11 | """ | |
| 12 | ||
| 13 | BABY = 0 | |
| 14 | SMALL = 1 | |
| 15 | LARGE = 2 | |
| 16 | PSMALL = 3 | |
| 17 | PLARGE = 4 | |
| 18 | ||
| 19 | def doit(preprocess, readinput, solve, MultiCore = True, Verify = False, Input = None, Filename = None, Problem = "A", Attempt = 0, SingleCase = False): | |
| 20 | start_time = time.time() | |
| 21 | ||
| 22 | if Input == None: | |
| 23 | if Filename == None: | |
| 24 | print "Input Filename Not specified." | |
| 25 | return | |
| 26 | INPUTNAME = Filename | |
| 27 | else: | |
| 28 | INPUTNAME = Problem + "_baby.in" | |
| 29 | if Input == SMALL: INPUTNAME = Problem + "-small-attempt" + str(Attempt) + ".in" | |
| 30 | if Input == LARGE: INPUTNAME = Problem + "-large.in" | |
| 31 | if Input == PSMALL: INPUTNAME = Problem + "-small-practice.in" | |
| 32 | if Input == PLARGE: INPUTNAME = Problem + "-large-practice.in" | |
| 33 | ||
| 34 | ||
| 35 | ||
| 36 | OUTPUTNAME = INPUTNAME[:INPUTNAME.find(".")] + ".out"
| |
| 37 | ||
| 38 | Input = File(INPUTNAME, "r") | |
| 39 | Correct = None | |
| 40 | ||
| 41 | if Verify == True: | |
| 42 | try: | |
| 43 | Output = File(OUTPUTNAME, "r") | |
| 44 | Correct = Output.readall() | |
| 45 | except IOError: | |
| 46 | print "Verification not possible - output file does not exist. Fall back to normal mode." | |
| 47 | Verify = False | |
| 48 | Output = File(OUTPUTNAME, "w") | |
| 49 | else: | |
| 50 | Output = File(OUTPUTNAME, "wb") | |
| 51 | ||
| 52 | PREPNAME = Problem + ".prep" | |
| 53 | try: | |
| 54 | pfile = open(PREPNAME, "rb") | |
| 55 | Prep = cPickle.load(pfile) | |
| 56 | pfile.close() | |
| 57 | print "Loaded preprocessed information from file" | |
| 58 | except IOError: | |
| 59 | Prep = preprocess() | |
| 60 | if Prep <> None: | |
| 61 | pfile = open(PREPNAME, "wb") | |
| 62 | cPickle.dump(Prep, pfile, 2) | |
| 63 | pfile.close() | |
| 64 | print "Saved preprocessed information to file" | |
| 65 | ||
| 66 | if SingleCase == True: | |
| 67 | n_cases = 1 | |
| 68 | else: | |
| 69 | n_cases = Input.readint() | |
| 70 | ||
| 71 | Problems = [] | |
| 72 | ||
| 73 | # Just reading input | |
| 74 | for _ in range(n_cases): | |
| 75 | Problems.append(readinput(Input)) | |
| 76 | ||
| 77 | AllCorrect = True | |
| 78 | ||
| 79 | if MultiCore: | |
| 80 | - | pool = multiprocessing.Pool(processes = 6) |
| 80 | + | pool = multiprocessing.Pool(processes = 4) |
| 81 | Result = [pool.apply_async(solve, [p, Prep]) for p in Problems] | |
| 82 | ||
| 83 | for n in range(n_cases): | |
| 84 | if MultiCore: | |
| 85 | Solution = Result[n].get() | |
| 86 | else: | |
| 87 | Solution = solve(Problems[n], Prep) | |
| 88 | ||
| 89 | sline = "Case #" + str(n+1) + ": " + str(Solution) | |
| 90 | ||
| 91 | print sline | |
| 92 | ||
| 93 | if not Verify: | |
| 94 | Output.write(sline + "\n") | |
| 95 | else: | |
| 96 | for line in split(sline, "\n"): | |
| 97 | if len(Correct) == 0 or line.rstrip("\n") <> Correct[0].rstrip("\n"):
| |
| 98 | print "MISMATCH: ", line, | |
| 99 | AllCorrect = False | |
| 100 | if len(Correct) > 0: print Correct[0] | |
| 101 | else: print "" | |
| 102 | if len(Correct) > 0: Correct.pop(0) | |
| 103 | ||
| 104 | TOT = (time.time() - start_time) / (n + 1) * n_cases | |
| 105 | if TOT > 120: | |
| 106 | print "Running over two minutes by:", int (TOT - 120), "seconds" | |
| 107 | ||
| 108 | print time.time() - start_time, "seconds" | |
| 109 | if Verify and AllCorrect == False: | |
| 110 | print "VERIFICATION FAILED - ERRORS WERE FOUND IN SOLUTION" | |
| 111 | ||
| 112 | return | |
| 113 | ||
| 114 | Primes = None | |
| 115 | largestPrime = None | |
| 116 | ||
| 117 | def frange(x, y, jump): | |
| 118 | while x < y: | |
| 119 | yield x | |
| 120 | x += jump | |
| 121 | ||
| 122 | def loadPrimes(N): | |
| 123 | global Primes | |
| 124 | global largestPrime | |
| 125 | - | pfile = open("Primes.prep", "rb")
|
| 125 | + | |
| 126 | pfile = open("Primes" + str(N) + ".prep", "rb")
| |
| 127 | Primes = cPickle.load(pfile) | |
| 128 | largestPrime = N | |
| 129 | - | if B > 15485863: |
| 129 | + | |
| 130 | except IOError: | |
| 131 | s = set(range(3, N + 1, 2)) | |
| 132 | Primes = [2] | |
| 133 | for i in range(3, N + 1, 2): | |
| 134 | if i not in s: | |
| 135 | continue | |
| 136 | ||
| 137 | Primes.append(i) | |
| 138 | ||
| 139 | k = 2 | |
| 140 | while k*i <= N: | |
| 141 | if k*i in s: | |
| 142 | s.remove(k*i) | |
| 143 | k += 1 | |
| 144 | pfile = open("Primes" + str(N) + ".prep", "wb")
| |
| 145 | cPickle.dump(Primes, pfile, 2) | |
| 146 | largestPrime = N | |
| 147 | return | |
| 148 | ||
| 149 | ||
| 150 | def getPrimes(A, B): | |
| 151 | global Primes | |
| 152 | global largestPrime | |
| 153 | """get primes between A and B inclusive""" | |
| 154 | if Primes == None: | |
| 155 | print "Prime numbers are not initialized" | |
| 156 | quit() | |
| 157 | ||
| 158 | if B > largestPrime: | |
| 159 | print "Sorry, no data on primes so large" | |
| 160 | quit() | |
| 161 | ||
| 162 | S = bisect.bisect_left(Primes, A) | |
| 163 | E = bisect.bisect_left(Primes, B + 1) | |
| 164 | return Primes[S:E] | |
| 165 | ||
| 166 | ||
| 167 | def ncr(n, r): | |
| 168 | r = min(r, n-r) | |
| 169 | if r == 0: return 1 | |
| 170 | if r < 0: return 0 | |
| 171 | num = reduce(mul, xrange(n, n-r, -1)) | |
| 172 | denom = reduce(mul, xrange(1, r+1)) | |
| 173 | return num/denom | |
| 174 | ||
| 175 | def memoize(f): | |
| 176 | cache= {}
| |
| 177 | def memf(*x): | |
| 178 | if x not in cache: | |
| 179 | cache[x] = f(*x) | |
| 180 | return cache[x] | |
| 181 | return memf | |
| 182 | ||
| 183 | def Transpose(M): | |
| 184 | X = len(M) | |
| 185 | Y = len(M[0]) | |
| 186 | A = [] | |
| 187 | for y in range(Y): | |
| 188 | B = [0] * X | |
| 189 | for x in range(X): | |
| 190 | B[x] = M[x][y] | |
| 191 | A.append(B) | |
| 192 | return A | |
| 193 | ||
| 194 | def bin_search_max(function, search_from, search_to, *args): | |
| 195 | """ | |
| 196 | function - function to test - must return True when result is OK. | |
| 197 | search_from - search from where (integer!) | |
| 198 | search_to - search to where (integer!) | |
| 199 | """ | |
| 200 | ||
| 201 | while search_from <> search_to: | |
| 202 | guess = (search_from + search_to) / 2 + 1 | |
| 203 | if function(guess, *args): | |
| 204 | search_from = guess | |
| 205 | else: | |
| 206 | search_to = guess - 1 | |
| 207 | ||
| 208 | return search_from | |
| 209 | ||
| 210 | def bin_search_max_float(function, search_from, search_to, precision, *args): | |
| 211 | """ | |
| 212 | function - function to test - must return True when result is OK. | |
| 213 | search_from - search from where (integer!) | |
| 214 | search_to - search to where (integer!) | |
| 215 | """ | |
| 216 | ||
| 217 | while abs(search_from - search_to) > precision: | |
| 218 | guess = (search_from + search_to) / 2.0 | |
| 219 | if function(guess, *args): | |
| 220 | search_from = guess | |
| 221 | else: | |
| 222 | search_to = guess | |
| 223 | ||
| 224 | return search_from | |
| 225 | ||
| 226 | def bin_search_min_float(function, search_from, search_to, precision, *args): | |
| 227 | """ | |
| 228 | function - function to test - must return True when result is OK. | |
| 229 | search_from - search from where (integer!) | |
| 230 | search_to - search to where (integer!) | |
| 231 | """ | |
| 232 | ||
| 233 | while abs(search_from - search_to) > precision: | |
| 234 | guess = (search_from + search_to) / 2.0 | |
| 235 | if function(guess, *args): | |
| 236 | search_to = guess | |
| 237 | else: | |
| 238 | search_from = guess | |
| 239 | ||
| 240 | return search_from | |
| 241 | ||
| 242 | def bin_search_min(function, search_from, search_to, *args): | |
| 243 | """ | |
| 244 | function - function to test - must return True when result is OK. | |
| 245 | search_from - search from where (integer!) | |
| 246 | search_to - search to where (integer!) | |
| 247 | """ | |
| 248 | ||
| 249 | while search_from <> search_to: | |
| 250 | guess = (search_from + search_to) / 2 | |
| 251 | if function(guess, *args): | |
| 252 | search_to = guess | |
| 253 | else: | |
| 254 | search_from = guess + 1 | |
| 255 | ||
| 256 | return search_from | |
| 257 | ||
| 258 | ||
| 259 | def bin_search(function, search_result, search_from, search_to, precision, *args): | |
| 260 | """ | |
| 261 | function - function to test | |
| 262 | search_result - what to look for (which number) | |
| 263 | search_from - search from where | |
| 264 | search_to - search to where | |
| 265 | precision - precision for comparison to test result | |
| 266 | """ | |
| 267 | W = search_to - search_from | |
| 268 | step = W / 4.0 | |
| 269 | initial = W / 2.0 + search_from | |
| 270 | s = function(initial, *args) | |
| 271 | ||
| 272 | while abs(step) > precision: | |
| 273 | if s > search_result and step > 0: | |
| 274 | step = - step / 2 | |
| 275 | if s < search_result and step < 0: | |
| 276 | step = - step / 2 | |
| 277 | initial = initial + step | |
| 278 | s = function(initial, *args) | |
| 279 | return initial | |
| 280 | ||
| 281 | def square_matrix_multiply(a, b): | |
| 282 | s = len(a) | |
| 283 | r = [] | |
| 284 | ||
| 285 | for x in range(s): | |
| 286 | r.append([0] * s) | |
| 287 | for y in range(s): | |
| 288 | for t in range(s): | |
| 289 | r[x][y] += (a[x][t] * b[t][y]) | |
| 290 | r[x][y] = r[x][y] | |
| 291 | ||
| 292 | return r | |
| 293 | ||
| 294 | def fastpower(m, x, p): | |
| 295 | """return a**b % q""" | |
| 296 | val = x | |
| 297 | mult = x | |
| 298 | p -= 1 | |
| 299 | ||
| 300 | while p > 0: | |
| 301 | odd = p & 1 # bitwise and | |
| 302 | if odd == 1: | |
| 303 | val = m(val, mult) | |
| 304 | p -= 1 | |
| 305 | if p == 0: | |
| 306 | break | |
| 307 | mult = m(mult, mult) | |
| 308 | p = p >> 1 # bitwise divide by 2 | |
| 309 | return val | |
| 310 | ||
| 311 | def feq(a, b): | |
| 312 | return fabs(a-b) < 1e-8 | |
| 313 | ||
| 314 | def next_permutation(L): | |
| 315 | B = len(L) - 1 | |
| 316 | while L[B] <= L[B-1]: | |
| 317 | B = B - 1 | |
| 318 | if (B == 0): | |
| 319 | return sorted(L) | |
| 320 | ||
| 321 | b = B | |
| 322 | for x in range(B, len(L)): | |
| 323 | if L[b] > L[x] and L[x] > L[B-1]: | |
| 324 | b = x | |
| 325 | ||
| 326 | (L[b], L[B - 1]) = (L[B - 1], L[b]) | |
| 327 | L = L[:B] + sorted(L[B:]) | |
| 328 | return L | |
| 329 | ||
| 330 | def semi_circle_segment(x0, x1, R): | |
| 331 | def integral(x, R): | |
| 332 | h = R - x | |
| 333 | return (R**2*acos((R-h)/R) - (R-h)*sqrt(2*R*h-h**2))/2 | |
| 334 | ||
| 335 | return - integral(x1, R) + integral(x0, R) | |
| 336 | ||
| 337 | def isqrt(x): | |
| 338 | if x < 0: | |
| 339 | raise ValueError('square root not defined for negative numbers')
| |
| 340 | n = int(x) | |
| 341 | if n == 0: | |
| 342 | return 0 | |
| 343 | a, b = divmod(n.bit_length(), 2) | |
| 344 | x = 2**(a+b) | |
| 345 | while True: | |
| 346 | y = (x + n//x)//2 | |
| 347 | if y >= x: | |
| 348 | return x | |
| 349 | x = y | |
| 350 | ||
| 351 | def ilog(x, b): | |
| 352 | if b == 1: | |
| 353 | raise "Log base 1 fail" | |
| 354 | a = -1 | |
| 355 | bb = 1 | |
| 356 | while bb <= x: | |
| 357 | bb *= b | |
| 358 | a += 1 | |
| 359 | return a | |
| 360 | ||
| 361 | class File: | |
| 362 | def __init__(self, filename, mode): | |
| 363 | self.handle = open(filename, mode) | |
| 364 | self.cursor = 0 | |
| 365 | self.filename = filename | |
| 366 | self.mode = mode | |
| 367 | if mode == "r": | |
| 368 | self.lines = self.handle.readlines() | |
| 369 | ||
| 370 | def __del__(self): | |
| 371 | self.handle.close() | |
| 372 | ||
| 373 | def readint(self): | |
| 374 | a = int(self.lines[self.cursor]) | |
| 375 | self.cursor += 1 | |
| 376 | return a | |
| 377 | ||
| 378 | def readall(self): | |
| 379 | return self.lines | |
| 380 | ||
| 381 | def readbinary(self): | |
| 382 | self.handle.close() | |
| 383 | self.handle = open(self.filename, "rb") | |
| 384 | return self.handle.read() | |
| 385 | ||
| 386 | def readints(self): | |
| 387 | ints = self.lines[self.cursor] | |
| 388 | ints = strip(ints, "\n") | |
| 389 | ints = strip(ints, " ") | |
| 390 | while " " in ints: | |
| 391 | ints = ints.replace(" ", " ")
| |
| 392 | ints = ints.split(" ")
| |
| 393 | for a in range(len(ints)): | |
| 394 | ints[a] = int(ints[a]) | |
| 395 | self.cursor += 1 | |
| 396 | return ints | |
| 397 | ||
| 398 | def readfloats(self): | |
| 399 | ints = self.lines[self.cursor] | |
| 400 | ints = strip(ints, "\n") | |
| 401 | ints = ints.split(" ")
| |
| 402 | for a in range(len(ints)): | |
| 403 | ints[a] = float(ints[a]) | |
| 404 | self.cursor += 1 | |
| 405 | return ints | |
| 406 | ||
| 407 | def readstring(self): | |
| 408 | a = self.lines[self.cursor] | |
| 409 | a = strip(a, "\n") | |
| 410 | self.cursor += 1 | |
| 411 | return a | |
| 412 | ||
| 413 | def readstrings(self): | |
| 414 | a = self.lines[self.cursor] | |
| 415 | a = strip(a, "\n") | |
| 416 | a = a.split(" ")
| |
| 417 | self.cursor += 1 | |
| 418 | return a | |
| 419 | ||
| 420 | def write(self, line): | |
| 421 | self.handle.write(line) | |
| 422 | return | |
| 423 | ||
| 424 | def writeint(self, number): | |
| 425 | self.handle.write(str(number)) | |
| 426 | return | |
| 427 | ||
| 428 | def writefloat(self, fl, after): | |
| 429 | self.handle.write(("%." + str(after) + "f") % fl)
| |
| 430 | return | |
| 431 | ||
| 432 | def nextline(self): | |
| 433 | self.handle.write("\n")
| |
| 434 | return | |
| 435 | ||
| 436 | ||
| 437 | """Matrix = numpy.array(Matrix, dtype = numpy.int64)""" | |
| 438 | ||
| 439 | ||
| 440 | class UnionFind: | |
| 441 | def __init__(self): | |
| 442 | '''\ | |
| 443 | Create an empty union find data structure.''' | |
| 444 | self.num_weights = {}
| |
| 445 | self.parent_pointers = {}
| |
| 446 | self.num_to_objects = {}
| |
| 447 | self.objects_to_num = {}
| |
| 448 | self.unique = 0 | |
| 449 | ||
| 450 | def insert_objects(self, objects): | |
| 451 | '''\ | |
| 452 | Insert a sequence of objects into the structure. All must be Python hashable.''' | |
| 453 | for object in objects: | |
| 454 | self.find(object); | |
| 455 | ||
| 456 | def find(self, object): | |
| 457 | '''\ | |
| 458 | Find the root of the set that an object is in. | |
| 459 | If the object was not known, will make it known, and it becomes its own set. | |
| 460 | Object must be Python hashable.''' | |
| 461 | if not object in self.objects_to_num: | |
| 462 | obj_num = len(self.objects_to_num) | |
| 463 | self.num_weights[obj_num] = 1 | |
| 464 | self.objects_to_num[object] = obj_num | |
| 465 | self.num_to_objects[obj_num] = object | |
| 466 | self.parent_pointers[obj_num] = obj_num | |
| 467 | self.unique += 1 | |
| 468 | return object | |
| 469 | stk = [self.objects_to_num[object]] | |
| 470 | par = self.parent_pointers[stk[-1]] | |
| 471 | while par != stk[-1]: | |
| 472 | stk.append(par) | |
| 473 | par = self.parent_pointers[par] | |
| 474 | for i in stk: | |
| 475 | self.parent_pointers[i] = par | |
| 476 | return self.num_to_objects[par] | |
| 477 | ||
| 478 | def union(self, object1, object2): | |
| 479 | '''\ | |
| 480 | Combine the sets that contain the two objects given. | |
| 481 | Both objects must be Python hashable. | |
| 482 | If either or both objects are unknown, will make them known, and combine them.''' | |
| 483 | o1p = self.find(object1) | |
| 484 | o2p = self.find(object2) | |
| 485 | if o1p != o2p: | |
| 486 | on1 = self.objects_to_num[o1p] | |
| 487 | on2 = self.objects_to_num[o2p] | |
| 488 | w1 = self.num_weights[on1] | |
| 489 | w2 = self.num_weights[on2] | |
| 490 | if w1 < w2: | |
| 491 | o1p, o2p, on1, on2, w1, w2 = o2p, o1p, on2, on1, w2, w1 | |
| 492 | self.num_weights[on1] = w1+w2 | |
| 493 | del self.num_weights[on2] | |
| 494 | self.parent_pointers[on2] = on1 | |
| 495 | self.unique -= 1 | |
| 496 | def getUnique(self): | |
| 497 | return self.unique | |
| 498 | ||
| 499 | ||
| 500 | class mod_calc: | |
| 501 | def __init__(self, m): | |
| 502 | self.m = m | |
| 503 | self.fact_table = None | |
| 504 | ||
| 505 | self.pre_fact(m) | |
| 506 | ||
| 507 | def pre_fact(self, m): | |
| 508 | f = 1 | |
| 509 | res = [1] | |
| 510 | for n in range(1, m): | |
| 511 | f = (f*n)%m | |
| 512 | res.append(f) | |
| 513 | return res | |
| 514 | ||
| 515 | def factorial(self, n): | |
| 516 | if self.fact_table == None: | |
| 517 | self.fact_table = self.pre_fact(self.m) | |
| 518 | if n==0: | |
| 519 | return 1 | |
| 520 | r = n%self.m | |
| 521 | k = n//self.m | |
| 522 | kk = 1 | |
| 523 | if k%2==1: | |
| 524 | kk = -1 | |
| 525 | return (self.fact_table[r]*self.factorial(k)*kk)%self.m | |
| 526 | ||
| 527 | def fact_ord(self, n): | |
| 528 | r = n%self.m | |
| 529 | k = n//self.m | |
| 530 | if k==0: | |
| 531 | return 0 | |
| 532 | return k+self.fact_ord(k) | |
| 533 | ||
| 534 | def gcd(self, a, b): | |
| 535 | if b==0: | |
| 536 | return [1,0] | |
| 537 | k = a//b | |
| 538 | r = a-b*k | |
| 539 | g = self.gcd(b, r) | |
| 540 | return [g[1], g[0]-g[1]*k] | |
| 541 | ||
| 542 | def inv(self, x): | |
| 543 | g = self.gcd(self.m, x) | |
| 544 | return g[1]%self.m | |
| 545 | ||
| 546 | def ncr(self, n, r): | |
| 547 | - | return lower[:-1] + upper[:-1] |
| 547 | + | |
| 548 | return 0 | |
| 549 | return (self.factorial(n)*self.inv((self.factorial(r)*self.factorial(n-r))%self.m))%self.m | |
| 550 | ||
| 551 | def rotate_coordinates_clockwise(xr, yr, alpha): | |
| 552 | """point stays still, coordinate axis rotate clockwise by angle alpha""" | |
| 553 | y = yr * math.cos(alpha) + xr * math.sin(alpha) | |
| 554 | x = xr * math.cos(alpha) - yr * math.sin(alpha) | |
| 555 | return x, y | |
| 556 | ||
| 557 | def point_left_line(a, b, c): | |
| 558 | """a and b are two points of line, showing direction. c is point to be determined""" | |
| 559 | r = (b[0] - a[0])*(c[1] - a[1]) - (b[1] - a[1])*(c[0] - a[0]) | |
| 560 | ||
| 561 | if r > 1e-15: | |
| 562 | return True | |
| 563 | ||
| 564 | elif r < -1e-15: | |
| 565 | return False | |
| 566 | ||
| 567 | return None | |
| 568 | ||
| 569 | def convex_hull(points): | |
| 570 | points = sorted(set(points)) | |
| 571 | if len(points) <= 1: | |
| 572 | return points | |
| 573 | def cross(o, a, b): | |
| 574 | return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) | |
| 575 | lower = [] | |
| 576 | for p in points: | |
| 577 | while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0: | |
| 578 | lower.pop() | |
| 579 | lower.append(p) | |
| 580 | upper = [] | |
| 581 | for p in reversed(points): | |
| 582 | while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0: | |
| 583 | upper.pop() | |
| 584 | upper.append(p) | |
| 585 | return lower[:-1] + upper[:-1] | |
| 586 | ||
| 587 | def findSCCs(Graph, n = None): | |
| 588 | """Graph with vertices numbered from 1...n, represented as Dictionary""" | |
| 589 | ||
| 590 | if n == None: | |
| 591 | n = len(Graph) | |
| 592 | ||
| 593 | po={}
| |
| 594 | ll={}
| |
| 595 | sf={}
| |
| 596 | sq = [] | |
| 597 | sl=[] | |
| 598 | i=0 | |
| 599 | for s in xrange(1, n+1): | |
| 600 | if s not in sf: | |
| 601 | queue=[s] | |
| 602 | while queue: | |
| 603 | v=queue[-1] | |
| 604 | if v not in po: | |
| 605 | i=i+1 | |
| 606 | po[v]=i | |
| 607 | done=1 | |
| 608 | v_nbrs=Graph.get(v, []) | |
| 609 | for w in v_nbrs: | |
| 610 | if w not in po: | |
| 611 | queue.append(w) | |
| 612 | done=0 | |
| 613 | break | |
| 614 | if done==1: | |
| 615 | ll[v]=po[v] | |
| 616 | for w in v_nbrs: | |
| 617 | if w not in sf: | |
| 618 | if po[w]>po[v]: | |
| 619 | ll[v]=min([ll[v],ll[w]]) | |
| 620 | else: | |
| 621 | ll[v]=min([ll[v],po[w]]) | |
| 622 | queue.pop() | |
| 623 | if ll[v]==po[v]: | |
| 624 | sf[v]=True | |
| 625 | scc=[v] | |
| 626 | while sq and po[sq[-1]]>po[v]: | |
| 627 | k=sq.pop() | |
| 628 | sf[k]=True | |
| 629 | scc.append(k) | |
| 630 | sl.append(scc) | |
| 631 | else: | |
| 632 | sq.append(v) | |
| 633 | return sl |