結果
| 問題 | No.2012 Largest Triangle |
| コンテスト | |
| ユーザー |
vwxyz
|
| 提出日時 | 2023-08-08 22:36:09 |
| 言語 | PyPy3 (7.3.15) |
| 結果 |
AC
|
| 実行時間 | 1,578 ms / 2,500 ms |
| コード長 | 27,210 bytes |
| コンパイル時間 | 386 ms |
| コンパイル使用メモリ | 82,624 KB |
| 実行使用メモリ | 160,156 KB |
| 最終ジャッジ日時 | 2024-11-14 03:24:07 |
| 合計ジャッジ時間 | 35,267 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 1 |
| other | AC * 41 |
ソースコード
import sys
readline=sys.stdin.readline
from collections import deque
# https://github.com/tatyam-prime/SortedSet/blob/main/SortedSet.py
import math
from bisect import bisect_left, bisect_right, insort
from typing import Generic, Iterable, Iterator, TypeVar, Optional, List
T = TypeVar('T')
class SortedSet(Generic[T]):
BUCKET_RATIO = 50
REBUILD_RATIO = 170
def _build(self, a: Optional[List[T]] = None) -> None:
"Evenly divide `a` into buckets."
if a is None: a = list(self)
size = self.size = len(a)
bucket_size = int(math.ceil(math.sqrt(size / self.BUCKET_RATIO)))
self.a = [a[size * i // bucket_size : size * (i + 1) // bucket_size] for i in range(bucket_size)]
def __init__(self, a: Iterable[T] = []) -> None:
"Make a new SortedSet from iterable. / O(N) if sorted and unique / O(N log N)"
a = list(a)
if not all(a[i] < a[i + 1] for i in range(len(a) - 1)):
a = sorted(set(a))
self._build(a)
def __iter__(self) -> Iterator[T]:
for i in self.a:
for j in i: yield j
def __reversed__(self) -> Iterator[T]:
for i in reversed(self.a):
for j in reversed(i): yield j
def __eq__(self, other) -> bool:
return list(self) == list(other)
def __len__(self) -> int:
return self.size
def __repr__(self) -> str:
return "SortedSet" + str(self.a)
def __str__(self) -> str:
s = str(list(self))
return "{" + s[1 : len(s) - 1] + "}"
def _find_bucket(self, x: T) -> List[T]:
"Find the bucket which should contain x. self must not be empty."
for a in self.a:
if x <= a[-1]: return a
return a
def __contains__(self, x: T) -> bool:
if self.size == 0: return False
a = self._find_bucket(x)
i = bisect_left(a, x)
return i != len(a) and a[i] == x
def add(self, x: T) -> bool:
"Add an element and return True if added. / O(√N)"
if self.size == 0:
self.a = [[x]]
self.size = 1
return True
a = self._find_bucket(x)
i = bisect_left(a, x)
if i != len(a) and a[i] == x: return False
a.insert(i, x)
self.size += 1
if len(a) > len(self.a) * self.REBUILD_RATIO:
self._build()
return True
def _pop(self, a: List[T], i: int) -> T:
ans = a.pop(i)
self.size -= 1
if not a: self._build()
return ans
def discard(self, x: T) -> bool:
"Remove an element and return True if removed. / O(√N)"
if self.size == 0: return False
a = self._find_bucket(x)
i = bisect_left(a, x)
if i == len(a) or a[i] != x: return False
self._pop(a, i)
return True
def lt(self, x: T) -> Optional[T]:
"Find the largest element < x, or None if it doesn't exist."
for a in reversed(self.a):
if a[0] < x:
return a[bisect_left(a, x) - 1]
def le(self, x: T) -> Optional[T]:
"Find the largest element <= x, or None if it doesn't exist."
for a in reversed(self.a):
if a[0] <= x:
return a[bisect_right(a, x) - 1]
def gt(self, x: T) -> Optional[T]:
"Find the smallest element > x, or None if it doesn't exist."
for a in self.a:
if a[-1] > x:
return a[bisect_right(a, x)]
def ge(self, x: T) -> Optional[T]:
"Find the smallest element >= x, or None if it doesn't exist."
for a in self.a:
if a[-1] >= x:
return a[bisect_left(a, x)]
def __getitem__(self, i: int) -> T:
"Return the i-th element."
if i < 0:
for a in reversed(self.a):
i += len(a)
if i >= 0: return a[i]
else:
for a in self.a:
if i < len(a): return a[i]
i -= len(a)
raise IndexError
def pop(self, i: int = -1) -> T:
"Pop and return the i-th element."
if i < 0:
for a in reversed(self.a):
i += len(a)
if i >= 0: return self._pop(a, i)
else:
for a in self.a:
if i < len(a): return self._pop(a, i)
i -= len(a)
raise IndexError
def index(self, x: T) -> int:
"Count the number of elements < x."
ans = 0
for a in self.a:
if a[-1] >= x:
return ans + bisect_left(a, x)
ans += len(a)
return ans
def index_right(self, x: T) -> int:
"Count the number of elements <= x."
ans = 0
for a in self.a:
if a[-1] > x:
return ans + bisect_right(a, x)
ans += len(a)
return ans
class SortedMultiset(Generic[T]):
BUCKET_RATIO = 50
REBUILD_RATIO = 170
def _build(self, a: Optional[List[T]] = None) -> None:
"Evenly divide `a` into buckets."
if a is None: a = list(self)
size = self.size = len(a)
bucket_size = int(math.ceil(math.sqrt(size / self.BUCKET_RATIO)))
self.a = [a[size * i // bucket_size : size * (i + 1) // bucket_size] for i in range(bucket_size)]
def __init__(self, a: Iterable[T] = []) -> None:
"Make a new SortedMultiset from iterable. / O(N) if sorted / O(N log N)"
a = list(a)
if not all(a[i] <= a[i + 1] for i in range(len(a) - 1)):
a = sorted(a)
self._build(a)
def __iter__(self) -> Iterator[T]:
for i in self.a:
for j in i: yield j
def __reversed__(self) -> Iterator[T]:
for i in reversed(self.a):
for j in reversed(i): yield j
def __eq__(self, other) -> bool:
return list(self) == list(other)
def __len__(self) -> int:
return self.size
def __repr__(self) -> str:
return "SortedMultiset" + str(self.a)
def __str__(self) -> str:
s = str(list(self))
return "{" + s[1 : len(s) - 1] + "}"
def _find_bucket(self, x: T) -> List[T]:
"Find the bucket which should contain x. self must not be empty."
for a in self.a:
if x <= a[-1]: return a
return a
def __contains__(self, x: T) -> bool:
if self.size == 0: return False
a = self._find_bucket(x)
i = bisect_left(a, x)
return i != len(a) and a[i] == x
def count(self, x: T) -> int:
"Count the number of x."
return self.index_right(x) - self.index(x)
def add(self, x: T) -> None:
"Add an element. / O(√N)"
if self.size == 0:
self.a = [[x]]
self.size = 1
return
a = self._find_bucket(x)
insort(a, x)
self.size += 1
if len(a) > len(self.a) * self.REBUILD_RATIO:
self._build()
def _pop(self, a: List[T], i: int) -> T:
ans = a.pop(i)
self.size -= 1
if not a: self._build()
return ans
def discard(self, x: T) -> bool:
"Remove an element and return True if removed. / O(√N)"
if self.size == 0: return False
a = self._find_bucket(x)
i = bisect_left(a, x)
if i == len(a) or a[i] != x: return False
self._pop(a, i)
return True
def lt(self, x: T) -> Optional[T]:
"Find the largest element < x, or None if it doesn't exist."
for a in reversed(self.a):
if a[0] < x:
return a[bisect_left(a, x) - 1]
def le(self, x: T) -> Optional[T]:
"Find the largest element <= x, or None if it doesn't exist."
for a in reversed(self.a):
if a[0] <= x:
return a[bisect_right(a, x) - 1]
def gt(self, x: T) -> Optional[T]:
"Find the smallest element > x, or None if it doesn't exist."
for a in self.a:
if a[-1] > x:
return a[bisect_right(a, x)]
def ge(self, x: T) -> Optional[T]:
"Find the smallest element >= x, or None if it doesn't exist."
for a in self.a:
if a[-1] >= x:
return a[bisect_left(a, x)]
def __getitem__(self, i: int) -> T:
"Return the i-th element."
if i < 0:
for a in reversed(self.a):
i += len(a)
if i >= 0: return a[i]
else:
for a in self.a:
if i < len(a): return a[i]
i -= len(a)
raise IndexError
def pop(self, i: int = -1) -> T:
"Pop and return the i-th element."
if i < 0:
for a in reversed(self.a):
i += len(a)
if i >= 0: return self._pop(a, i)
else:
for a in self.a:
if i < len(a): return self._pop(a, i)
i -= len(a)
raise IndexError
def index(self, x: T) -> int:
"Count the number of elements < x."
ans = 0
for a in self.a:
if a[-1] >= x:
return ans + bisect_left(a, x)
ans += len(a)
return ans
def index_right(self, x: T) -> int:
"Count the number of elements <= x."
ans = 0
for a in self.a:
if a[-1] > x:
return ans + bisect_right(a, x)
ans += len(a)
return ans
class Convex_Hull_Trick:
def __init__(self,avl=False):
self.avl=avl
if self.avl:
self.lines=AVLTree_dict()
else:
self.lines_A=SortedSet()
self.lines_B={}
def is_removed(self,line0,line1,line2):
if line0==None:
return False
if line2==None:
return False
a0,b0=line0
a1,b1=line1
a2,b2=line2
return (a1-a0)*(b2-b1)<=(b1-b0)*(a2-a1)
def add_line(self,a,b):
if self.avl:
bb=self.lines[a]
else:
if a in self.lines_B:
bb=self.lines_B[a]
else:
bb=None
if bb!=None and bb<=b:
return
if self.avl:
line0=self.lines.Bisect_Left(a)
else:
aa=self.lines_A.lt(a)
if aa==None:
line0=None
else:
line0=(aa,self.lines_B[aa])
line1=(a,b)
if self.avl:
line2=self.lines.Bisect_Right(a)
else:
aa=self.lines_A.gt(a)
if aa==None:
line2=None
else:
line2=(aa,self.lines_B[aa])
if self.is_removed(line0,line1,line2):
return
if self.avl:
self.lines[a]=b
else:
self.lines_A.add(a)
self.lines_B[a]=b
line2=(a,b)
if self.avl:
line1=self.lines.Bisect_Left(line2[0])
line0=None if line1==None else self.lines.Bisect_Left(line1[0])
else:
aa=self.lines_A.lt(line2[0])
if aa==None:
line1=None
else:
line1=(aa,self.lines_B[aa])
if line1==None:
line0=None
else:
aa=self.lines_A.lt(line1[0])
if aa==None:
line0=None
else:
line0=(aa,self.lines_B[aa])
while self.is_removed(line0,line1,line2):
if self.avl:
del self.lines[line1[0]]
else:
assert self.lines_A.discard(line1[0])
del self.lines_B[line1[0]]
line1=line0
if self.avl:
line0=None if line1==None else self.lines.Bisect_Left(line1[0])
else:
aa=self.lines_A.lt(line1[0])
if aa==None:
line0=None
else:
line0=(aa,self.lines_B[aa])
line0=(a,b)
if self.avl:
line1=self.lines.Bisect_Right(line0[0])
line2=None if line1==None else self.lines.Bisect_Right(line1[0])
else:
aa=self.lines_A.gt(line0[0])
if aa==None:
line1=None
else:
line1=(aa,self.lines_B[aa])
if line1==None:
line2=None
else:
aa=self.lines_A.gt(line1[0])
if aa==None:
line2=None
else:
line2=(aa,self.lines_B[aa])
while self.is_removed(line0,line1,line2):
if self.avl:
del self.lines[line1[0]]
else:
assert self.lines_A.discard(line1[0])
del self.lines_B[line1[0]]
line1=line2
if self.avl:
line2=None if line1==None else self.lines.Bisect_Right(line1[0])
else:
aa=self.lines_A.gt(line1[0])
if aa==None:
line2=None
else:
line2=(aa,self.lines_B[aa])
def __call__(self,x,y):
if self.avl:
size=len(self.lines)
else:
size=len(self.lines_A)
if not size:
return None
ok,ng=-1,size-1
while ng-ok>1:
mid=(ok+ng)//2
if self.avl:
a0,b0=self.lines.Find_Kth_Element(mid)
a1,b1=self.lines.Find_Kth_Element(mid+1)
else:
a0=self.lines_A[mid]
b0=self.lines_B[a0]
a1=self.lines_A[mid+1]
b1=self.lines_B[a1]
if a0*x+b0*y>a1*x+b1*y:
ok=mid
else:
ng=mid
if self.avl:
a,b=self.lines.Find_Kth_Element(ok+1)
else:
a=self.lines_A[ok+1]
b=self.lines_B[a]
return a*x+b*y
def __getitem__(self,a):
if self.avl:
return self.lines[a]
else:
if a in self.lines_A:
return self.lines_B[a]
else:
return None
def __setitem__(self,a,b):
self.add_line(a,b)
class Cumsum:
def __init__(self,lst,mod=0):
self.N=len(lst)
self.mod=mod
self.cumsum=[0]*(self.N+1)
self.cumsum[0]=0
for i in range(1,self.N+1):
self.cumsum[i]=self.cumsum[i-1]+lst[i-1]
if self.mod:
self.cumsum[i]%=self.mod
def __getitem__(self,i):
if type(i)==int:
if 0<=i<self.N:
a,b=i,i+1
elif -self.N<=i<0:
a,b=i+self.N,i+self.N+1
else:
raise IndexError('list index out of range')
else:
a,b=i.start,i.stop
if a==None or a<-self.N:
a=0
elif self.N<=a:
a=self.N
elif a<0:
a+=self.N
if b==None or self.N<=b:
b=self.N
elif b<-self.N:
b=0
elif b<0:
b+=self.N
s=self.cumsum[b]-self.cumsum[a]
if self.mod:
s%=self.mod
return s
def __setitem__(self,i,x):
if -self.N<=i<0:
i+=self.N
elif not 0<=i<self.N:
raise IndexError('list index out of range')
self.cumsum[i+1]=self.cumsum[i]+x
if self.mod:
self.cumsum[i+1]%=self.mod
def __len__(self):
return self.N
def __str__(self):
lst=[self.cumsum[i+1]-self.cumsum[i] for i in range(self.N)]
if self.mod:
for i in range(self.N):
lst[i]%=self.mod
return "["+", ".join(map(str,lst))+"]"
class AVL_Node_dict:
"""ノード
Attributes:
key (any): ノードのキー。比較可能なものであれば良い。(1, 4)などタプルも可。
val (any): ノードの値。
left (Node): 左の子ノード。
right (Node): 右の子ノード。
bias (int): 平衡度。(左部分木の高さ)-(右部分木の高さ)。
size (int): 自分を根とする部分木の大きさ
"""
def __init__(self,parent,key,value):
self.parent=parent
self.key=key
self.value=value
self.left=None
self.right=None
self.bias=0
self.size=1
class AVLTree_dict:
def __init__(self):
self.root=None
def Rotate_Left(self,node):
node_right=node.right
node_right.size=node.size
node.size-=1
if node_right.right!=None:
node.size-=node_right.right.size
if node_right.bias==-1:
node_right.bias=0
node.bias=0
else:
#assert node_right.bias==0
node_right.bias=1
node.bias=-1
node.right=node_right.left
node_right.left=node
return node_right
def Rotate_Right(self,node):
node_left=node.left
node_left.size=node.size
node.size-=1
if node_left.left!=None:
node.size-=node_left.left.size
if node_left.bias==1:
node_left.bias=0
node.bias=0
else:
#assert node_left.bias==0
node_left.bias=-1
node.bias=1
node.left=node_left.right
node_left.right=node
return node_left
def Rotate_Left_Right(self,node):
node_left=node.left
node_left_right=node_left.right
#assert node.bias==2
#assert node_left.bias==-1
#assert node_left_right.bias in (-1,0,1)
node_left_right.size=node.size
node.size-=node_left.size
if node_left_right.right!=None:
node.size+=node_left_right.right.size
node_left.size-=1
if node_left_right.right!=None:
node_left.size-=node_left_right.right.size
node_left.right=node_left_right.left
node_left_right.left=node_left
node.left=node_left_right.right
node_left_right.right=node
self.Update_Bias_Double(node_left_right)
return node_left_right
def Rotate_Right_Left(self,node):
node_right=node.right
node_right_left=node_right.left
#assert node.bias==-2
#assert node_right.bias==1
#assert node_right_left.bias in (-1,0,1)
node_right_left.size=node.size
node.size-=node_right.size
if node_right_left.left!=None:
node.size+=node_right_left.left.size
node_right.size-=1
if node_right_left.left!=None:
node_right.size-=node_right_left.left.size
node_right.left=node_right_left.right
node_right_left.right=node_right
node.right=node_right_left.left
node_right_left.left=node
self.Update_Bias_Double(node_right_left)
return node_right_left
def Update_Bias_Double(self,node):
#assert node.right.bias*node.left.bias==-2
#assert node.right.bias>0
if node.bias==1:
node.right.bias=-1
node.left.bias=0
elif node.bias==-1:
node.right.bias=0
node.left.bias=1
else:
node.right.bias=0
node.left.bias=0
node.bias=0
def __getitem__(self,key):
v=self.root
while v!=None:
if key<v.key:
v=v.left
elif v.key<key:
v=v.right
else:
return v.value
return None
def __setitem__(self,key,value):
if self.root==None:
self.root=AVL_Node_dict(None,key,value)
return
v=self.root
stack=[]
while v!=None:
if key<v.key:
stack.append((v,1))
v=v.left
elif v.key<key:
stack.append((v,-1))
v=v.right
elif v.key==key:
v.value=value
return
p,direction=stack[-1]
if direction==1:
p.left=AVL_Node_dict(p,key,value)
else:
p.right=AVL_Node_dict(p,key,value)
while stack:
v,direction=stack.pop()
v.bias+=direction
v.size+=1
vv=None
if v.bias==2:
if v.left.bias==-1:
vv=self.Rotate_Left_Right(v)
else:
vv=self.Rotate_Right(v)
#assert vv!=None
break
if v.bias==-2:
if v.right.bias==1:
vv=self.Rotate_Right_Left(v)
else:
vv=self.Rotate_Left(v)
#assert vv!=None
break
if v.bias==0:
break
if vv!=None:
if len(stack)==0:
self.root=vv
return
p,direction=stack.pop()
p.size+=1
if direction==1:
p.left=vv
else:
p.right=vv
while stack:
p,direction=stack.pop()
p.size+=1
def __delitem__(self,key):
v=self.root
stack=[]
while v!=None:
if key<v.key:
stack.append((v,1))
v=v.left
elif v.key<key:
stack.append((v,-1))
v=v.right
else:
break
else:
return False
if v.left!=None:
stack.append((v,1))
lmax=v.left
while lmax.right!=None:
stack.append((lmax,-1))
lmax=lmax.right
v.key=lmax.key
v.value=lmax.value
v=lmax
c=v.right if v.left==None else v.left
if stack:
p,direction=stack[-1]
if direction==1:
p.left=c
else:
p.right=c
else:
self.root=c
return True
while stack:
pp=None
p,direction=stack.pop()
p.bias-=direction
p.size-=1
if p.bias==2:
if p.left.bias==-1:
pp=self.Rotate_Left_Right(p)
else:
pp=self.Rotate_Right(p)
elif p.bias==-2:
if p.right.bias==1:
pp=self.Rotate_Right_Left(p)
else:
pp=self.Rotate_Left(p)
elif p.bias!=0:
break
if pp!=None:
if len(stack)==0:
self.root=pp
return True
p,direction=stack[-1]
if direction==1:
p.left=pp
else:
p.right=pp
if pp.bias!=0:
break
while stack:
p,direction=stack.pop()
p.size-=1
return True
def __contains__(self,key):
v=self.root
while v!=None:
if key<v.key:
v=v.left
elif v.key<key:
v=v.right
else:
return True
return False
def Bisect_Right(self,key):
retu=None
v=self.root
while v!=None:
if v.key>key:
if retu==None or retu[0]>v.key:
retu=(v.key,v.value)
v=v.left
else:
v=v.right
return retu
def Bisect_Left(self,key):
retu=None
v=self.root
while v!=None:
if v.key<key:
if retu==None or retu[0]<v.key:
retu=(v.key,v.value)
v=v.right
else:
v=v.left
return retu
def Find_Kth_Element(self,K):
v=self.root
s=0
while v!=None:
t=s+v.left.size if v.left!=None else s
if t==K:
return v.key,v.value
elif t<K:
s=t+1
v=v.right
else:
v=v.left
return None
def keys(self):
stack=[(self.root,True)]
while stack:
node,subtree=stack.pop()
if subtree:
if node.right!=None:
stack.append((node.right,True))
stack.append((node,False))
if node.left!=None:
stack.append((node.left,True))
else:
yield node.key
def values(self):
stack=[(self.root,True)]
while stack:
node,subtree=stack.pop()
if subtree:
if node.right!=None:
stack.append((node.right,True))
stack.append((node,False))
if node.left!=None:
stack.append((node.left,True))
else:
yield node.value
def items(self):
stack=[(self.root,True)]
while stack:
node,subtree=stack.pop()
if subtree:
if node.right!=None:
stack.append((node.right,True))
stack.append((node,False))
if node.left!=None:
stack.append((node.left,True))
else:
yield (node.key,node.value)
def __bool__(self):
return self.root!=None
def __len__(self):
return 0 if self.root==None else self.root.size
def __iter__(self):
return iter(self.keys())
def __str__(self):
if self.root==None:
retu="{}"
else:
retu="{"+", ".join(f"{r}: {m}" for r,m in self.items())+"}"
return retu
class Convex_Hull_Trick_deque:
"""
f_i = a_ix + b_i とする。f_i の追加および、min_i f(x) の取得ができるデータ構造。
ただし、傾き a_i は降順に追加されなければならない。
また、クエリ x も昇順に実行されなければならない。
"""
def __init__(self):
self.lines=deque()
def add_line(self,a,b):
lines=self.lines
while len(lines) >= 2:
a1,b1=lines[-2]
a2,b2=lines[-1]
if (a2-a1)*(b-b2)<(b2-b1)*(a-a2):
break
lines.pop()
lines.append((a, b))
def __call__(self, x,y):
lines=self.lines
a,b=lines[0]
z=a*x+b*y
while len(lines) >= 2:
a2, b2 = lines[1]
z2 = a2 * x + b2*y
if z < z2:
break
z = z2
lines.popleft()
return z
N=int(readline())
X,Y=[],[]
CHT=Convex_Hull_Trick_deque()
for n in range(N):
x,y=map(int,readline().split())
X.append(x)
Y.append(y)
for y,x in sorted([(-Y[i],X[i]) for i in range(N)]+[(Y[i],-X[i]) for i in range(N)],reverse=True,key=lambda tpl:tpl[0]):
CHT.add_line(y,x)
ans=max(-CHT(x,y) for x,y in sorted([(X[i],Y[i]) for i in range(N)]+[(-X[i],-Y[i]) for i in range(N)],key=lambda tpl:tpl[0]))
print(ans)
vwxyz