Advertisement
vbuterin

Codesprint Direct Connections n^1.5 Solution

Jan 9th, 2012
304
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.25 KB | None | 0 0
  1. import math
  2. cases = int(raw_input())
  3. def binsearch(li,score):
  4.   left,right = 0,len(li)-1
  5.   if right < left: return 0
  6.   while right-left > 1:
  7.     ave = int((left+right)/2)
  8.     if score < li[ave][1]: right = ave # Ascending
  9.     else: left = ave
  10.   if score <= li[left][1]: return left
  11.   elif score <= li[right][1]: return right
  12.   else: return right + 1
  13. for case in range(cases):
  14.   citiesnum = int(raw_input())
  15.   coords = [int(x) for x in raw_input().split(' ')[:citiesnum]]
  16.   pops = [int(x) for x in raw_input().split(' ')[:citiesnum]]
  17.   # Bypop: cities sorted by population (high to low)
  18.   bypop = []
  19.   for x in range(citiesnum): bypop.append([pops[x],coords[x]]) # Pop = [i][0], coord = [i][1]
  20.   bypop.sort(reverse=True,key=lambda i: i[0])
  21.   # Bycoord: cities sorted by coordinates (left to right)
  22.   bycoord = sorted(bypop,key=lambda i: i[1])
  23.   batchsize = int(math.sqrt(citiesnum))
  24.   # Cities sorted by population in batches (ie. [a b c d e f g h i j] -> [[a b c][d e f][g h i][j]]; this is done to reduce
  25.   # the time of some operations from O(n) to O(sqrt(n)), and is the reason why this algorithm outperforms the naive n^2 one so greatly
  26.   batches = []
  27.   # Running total of batch coordinates (eg. 1 2 3 4 5 6 7 8 9 10 -> 6 21 45 55
  28.   macrosums = []
  29.   # Running total of city coordinates within each batch (eg. 1 2 3 4 5 6 7 8 9 10 -> [1 3 6][4 9 15][7 15 24][10]
  30.   microsums = []
  31.   for i in range(0,citiesnum,batchsize):
  32.      microsums.append([])
  33.      batches.append([])
  34.      newsum = 0
  35.      for j in range(i,min(i+batchsize,citiesnum)):
  36.        batches[-1].append(bycoord[j])
  37.        newsum += bycoord[j][1]
  38.        microsums[-1].append(newsum)
  39.      macrosums.append((0 if i == 0 else macrosums[-1]) + newsum)
  40.   total = 0
  41.   remaining = citiesnum
  42.   for i in range(citiesnum-1):
  43.     # Find ith largest city in bycoord, then in batches (outer,inner), get running coordinate sum
  44.     bycoord_place = binsearch(bycoord,bypop[i][1])
  45.     outer = int(bycoord_place / batchsize)
  46.     inner = binsearch(batches[outer],bypop[i][1])
  47.     sumat = (0 if outer == 0 else macrosums[outer - 1]) + microsums[outer][inner]
  48.     # Find previous city in coordinate order in batches
  49.     if outer > 0 or inner > 0:
  50.       innerprev,outerprev = inner - 1,outer
  51.       while innerprev < 0 and outerprev >= 0:
  52.         outerprev -= 1
  53.         innerprev = len(batches[outerprev])-1
  54.       if outerprev < 0: sumprev = 0
  55.       else: sumprev = (0 if outerprev == 0 else macrosums[outerprev - 1]) + microsums[outerprev][innerprev]
  56.     else: outerprev,innerprev,sumprev = -1,-1,0
  57.     place = inner
  58.     for j in range(outer): place += len(batches[j])
  59.     # The basic algorithm works like this:
  60.     # Take some cities:          1 2 3 5  7  9  12 15
  61.     # Get their coordinate sums: 1 3 6 11 18 27 39 54
  62.     # We're looking to find the sum of all distances from, say, the city at 9 to all the others.
  63.     # The answer is (15-9)+(12-9)+(9-7)+(9-5)+(9-3)+(9-2)+(9-1)
  64.     # Rewriting that, we get (15+12) - (1+2+3+5+7) + (9+9+9+9+9) - (9+9)
  65.     # Or (let S(n) be the sum of all cities left of n): S(15) - S(9) - S(7) + 9(10-7), which is what the nt = line below calculates
  66.     # The 10-7 arises from the fact that there are 5 cities to the left, which are added, and 2 to the right, which are subtracted.
  67.     # An alternative way of seeing that is to subtract all 7 other cities (-7) and then add all those to the left twice (+10)
  68.     nt = macrosums[-1] - sumat - (0 if bycoord_place == 0 else sumprev) + bycoord[bycoord_place][1] * (place * 2 + 1 - remaining)
  69.     # Remove the used city from the batches because we don't need to count it again and adjust the sums to account for this
  70.     # eg. ... 18 27 39 54 -> ... 18 30 45
  71.     # It's the adjusting of the sums that makes buckets necessary - if it weren't for buckets, adjusting sums would be an O(n) process,
  72.     # making the entire algorithm O(n^2); buckets reduce the runtime to O(sqrt(n))
  73.     for j in range(inner,len(batches[outer])): microsums[outer][j] -= bycoord[bycoord_place][1]
  74.     for j in range(outer,len(batches)): macrosums[j] -= bycoord[bycoord_place][1]
  75.     batches[outer].pop(inner)
  76.     microsums[outer].pop(inner)
  77.     remaining -= 1
  78.     total += nt * bypop[i][0]
  79.     # Mandated by problem specifications
  80.     total = total % 1000000007
  81.   print total
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement