Submission #9107531
Source Code Expand
;; -*- coding: utf-8 -*-
(eval-when (:compile-toplevel :load-toplevel :execute)
(sb-int:defconstant-eqx OPT
#+swank '(optimize (speed 3) (safety 2))
#-swank '(optimize (speed 3) (safety 0) (debug 0))
#'equal)
#+swank (ql:quickload '(:cl-debug-print :fiveam) :silent t)
#-swank (set-dispatch-macro-character
;; enclose the form with VALUES to avoid being captured by LOOP macro
#\# #\> (lambda (s c p) (declare (ignore c p)) `(values ,(read s nil nil t)))))
#+swank (cl-syntax:use-syntax cl-debug-print:debug-print-syntax)
#-swank (disable-debugger) ; for CS Academy
;; BEGIN_INSERTED_CONTENTS
(defmacro with-buffered-stdout (&body body)
"Buffers all outputs to *STANDARD-OUTPUT* in BODY and flushes them to
*STANDARD-OUTPUT* after BODY has been done (without error). Note that only
BASE-CHAR is allowed."
(let ((out (gensym)))
`(let ((,out (make-string-output-stream :element-type 'base-char)))
(let ((*standard-output* ,out))
,@body)
(write-string (get-output-stream-string ,out)))))
;;;
;;; 2D range tree on an arbitrary commutative monoid
;;;
;;; build: O(nlog^2(n))
;;; query: O(log^2(n))
;;;
;;; Reference:
;;; https://www.cse.wustl.edu/~taoju/cse546/lectures/Lecture21_rangequery_2d.pdf
;;;
;; TODO: map all the points in a given rectangle
;; TODO: k-dimensional range tree
(declaim (inline op))
(defun op (a b)
"Is a binary operator comprising a commutative monoid"
(declare (fixnum a b))
(min a b))
(defconstant +op-identity+ most-positive-fixnum
"identity element w.r.t. OP")
(defstruct (xnode (:constructor make-xnode (xkey ynode left right))
(:conc-name %xnode-)
(:copier nil))
(xkey 0 :type fixnum)
ynode
left right)
(defstruct (ynode (:constructor make-ynode (xkey ykey left right &key (count 1) value accumulator))
(:conc-name %ynode-)
(:copier nil))
(xkey 0 :type fixnum)
(ykey 0 :type fixnum)
left
right
(count 1 :type (integer 0 #.most-positive-fixnum))
(value +op-identity+ :type fixnum)
(accumulator +op-identity+ :type fixnum))
(declaim (inline ynode-count))
(defun ynode-count (ynode)
"Returns the number of the elements."
(if (null ynode)
0
(%ynode-count ynode)))
(declaim (inline ynode-accumulator))
(defun ynode-accumulator (ynode)
(if (null ynode)
+op-identity+
(%ynode-accumulator ynode)))
(declaim (inline ynode-update-count))
(defun ynode-update-count (ynode)
(setf (%ynode-count ynode)
(+ 1
(ynode-count (%ynode-left ynode))
(ynode-count (%ynode-right ynode)))))
(declaim (inline ynode-update-accumulator))
(defun ynode-update-accumulator (ynode)
(setf (%ynode-accumulator ynode)
(op (op (ynode-accumulator (%ynode-left ynode))
(%ynode-value ynode))
(ynode-accumulator (%ynode-right ynode)))))
(declaim (inline force-up))
(defun force-up (ynode)
"Propagates the information up from children."
(ynode-update-count ynode)
(ynode-update-accumulator ynode))
;;
;; Merging w.r.t. Y-axis in O(n) time:
;; 1. transform two trees to two paths (with copying);
;; 2. merge the two paths into a path (destructively);
;; 3. transform the path to a tree (destructively);
;;
(declaim (inline %ynode-to-path))
(defun %ynode-to-path (ynode)
"Returns a path that is equivalent to YNODE but in reverse order."
(declare (inline make-ynode))
(let ((res nil))
(labels ((recur (node)
(when node
(recur (%ynode-left node))
(setq res (make-ynode (%ynode-xkey node) (%ynode-ykey node) nil res
:value (%ynode-value node)
:accumulator (%ynode-value node)))
(recur (%ynode-right node)))))
(recur ynode)
res)))
(declaim (inline %ynode-merge-path!))
(defun %ynode-merge-path! (ypath1 ypath2)
"Destructively merges two pathes in reverse order."
(let ((res nil))
(macrolet ((%push (y)
`(let ((rest (%ynode-right ,y)))
(setf (%ynode-right ,y) res
res ,y
,y rest))))
(loop (unless ypath1
(loop while ypath2 do (%push ypath2))
(return))
(unless ypath2
(loop while ypath1 do (%push ypath1))
(return))
;; I use only #'< here for abstraction in the future
(if (or (< (%ynode-ykey ypath1) (%ynode-ykey ypath2))
(and (not (< (%ynode-ykey ypath2) (%ynode-ykey ypath1)))
(< (%ynode-xkey ypath1) (%ynode-xkey ypath2))))
(%push ypath2)
(%push ypath1)))
res)))
(declaim (inline %path-to-ynode!))
(defun %path-to-ynode! (ypath length)
"Destructively transforms a path to a balanced binary tree."
(declare ((integer 0 #.most-positive-fixnum) length))
(let* ((max-depth (- (integer-length length) 1)))
(macrolet ((%pop ()
`(let ((rest (%ynode-right ypath))
(first ypath))
(setf (%ynode-right first) nil
ypath rest)
first)))
(labels ((build (depth)
(declare ((integer 0 #.most-positive-fixnum) depth))
(when ypath
(if (= depth max-depth)
(%pop)
(let ((left (build (+ 1 depth))))
(if (null ypath)
left
(let* ((node (%pop))
(right (build (+ 1 depth))))
(setf (%ynode-left node) left)
(setf (%ynode-right node) right)
(force-up node)
node)))))))
(build 0)))))
(defun %ynode-merge (ynode1 ynode2)
"Merges two YNODEs non-destructively in O(n)."
(declare (optimize (speed 3) (safety 0)))
(let* ((length (+ (ynode-count ynode1) (ynode-count ynode2))))
(declare (fixnum length))
(%path-to-ynode!
(%ynode-merge-path! (%ynode-to-path ynode1)
(%ynode-to-path ynode2))
length)))
(declaim (inline make-range-tree))
(defun make-range-tree (points &key (xkey #'car) (ykey #'cdr) value-key)
"points := vector of poins
Makes a range tree from the points. These points must be sorted
w.r.t. lexicographical order and must not contain duplicate points. (Duplicate
coordinates are allowed.) E.g. (-1, 3), (-1, 4), (-1, 7) (0, 1) (0, 3) (2,
-1) (2, 1)).
If VALUE-KEY is given, the i-th point is bounded to the value (FUNCALL VALUE-KEY
POINTS[i]), otherwise to the value +OP-IDENTITY+."
(declare ((simple-array list (*)) points))
(labels ((build (l r)
(declare ((integer 0 #.most-positive-fixnum) l r))
(if (= (- r l) 1)
(let* ((point (aref points l))
(x (funcall xkey point))
(y (funcall ykey point))
(value (if value-key (funcall value-key point) +op-identity+)))
(make-xnode x (make-ynode x y nil nil
:value value
:accumulator value)
nil nil))
(let* ((mid (ash (+ l r) -1))
(left (build l mid))
(right (build mid r)))
(make-xnode (funcall xkey (aref points mid))
(%ynode-merge (%xnode-ynode left)
(%xnode-ynode right))
left right)))))
(build 0 (length points))))
(defconstant +neg-inf+ most-negative-fixnum)
(defconstant +pos-inf+ most-positive-fixnum)
(declaim (inline xleaf-p))
(defun xleaf-p (xnode)
(and (null (%xnode-left xnode)) (null (%xnode-right xnode))))
;; Below is almost the same as RT-COUNT. Is it better to integrate them?
(defun rt-query (range-tree x1 y1 x2 y2)
"Queries the `sum' of the nodes in the rectangle [x1, y1)*[x2, y2). A part or
all of these coordinates can be NIL; then they are regarded as the negative or
positive infinity."
(declare #.OPT
((or null fixnum) x1 y1 x2 y2))
(setq x1 (or x1 +neg-inf+)
x2 (or x2 +pos-inf+)
y1 (or y1 +neg-inf+)
y2 (or y2 +pos-inf+))
(labels ((xrecur (xnode x1 x2)
(declare ((or null xnode) xnode)
(fixnum x1 x2)
;; KLUDGE: declaring ftype is not sufficient for the
;; optimization on SBCL 1.1.14.
#+sbcl (values (integer 0 #.most-positive-fixnum)))
(cond ((null xnode) +op-identity+)
((and (= x1 +neg-inf+) (= x2 +pos-inf+))
(yrecur (%xnode-ynode xnode) y1 y2))
(t
(let ((xkey (%xnode-xkey xnode)))
(if (<= x1 xkey)
(if (< xkey x2)
;; XKEY is in [X1, X2)
(if (xleaf-p xnode)
(yrecur (%xnode-ynode xnode) y1 y2)
(op (xrecur (%xnode-left xnode) x1 +pos-inf+)
(xrecur (%xnode-right xnode) +neg-inf+ x2)))
;; XKEY is in [X2, +inf)
(xrecur (%xnode-left xnode) x1 x2))
;; XKEY is in (-inf, X1)
(xrecur (%xnode-right xnode) x1 x2))))))
(yrecur (ynode y1 y2)
(declare ((or null ynode) ynode)
(fixnum y1 y2)
#+sbcl (values (integer 0 #.most-positive-fixnum)))
(cond ((null ynode) +op-identity+)
((and (= y1 +neg-inf+) (= y2 +pos-inf+))
(%ynode-accumulator ynode))
(t
(let ((key (%ynode-ykey ynode)))
(if (<= y1 key)
(if (< key y2)
(op (op (yrecur (%ynode-left ynode) y1 +pos-inf+)
(%ynode-value ynode))
(yrecur (%ynode-right ynode) +neg-inf+ y2))
(yrecur (%ynode-left ynode) y1 y2))
(yrecur (%ynode-right ynode) y1 y2)))))))
;; (declare (ftype (function * (values (integer 0 #.most-positive-fixnum) &optional)) xrecur yrecur))
(xrecur range-tree x1 x2)))
(declaim (ftype (function * (values fixnum &optional)) read-fixnum))
(defun read-fixnum (&optional (in *standard-input*))
(declare #.OPT)
(macrolet ((%read-byte ()
`(the (unsigned-byte 8)
#+swank (char-code (read-char in nil #\Nul))
#-swank (sb-impl::ansi-stream-read-byte in nil #.(char-code #\Nul) nil))))
(let* ((minus nil)
(result (loop (let ((byte (%read-byte)))
(cond ((<= 48 byte 57)
(return (- byte 48)))
((zerop byte) ; #\Nul
(error "Read EOF or #\Nul."))
((= byte #.(char-code #\-))
(setf minus t)))))))
(declare ((integer 0 #.most-positive-fixnum) result))
(loop
(let* ((byte (%read-byte)))
(if (<= 48 byte 57)
(setq result (+ (- byte 48)
(* 10 (the (integer 0 #.(floor most-positive-fixnum 10)) result))))
(return (if minus (- result) result))))))))
(defmacro dbg (&rest forms)
#+swank
(if (= (length forms) 1)
`(format *error-output* "~A => ~A~%" ',(car forms) ,(car forms))
`(format *error-output* "~A => ~A~%" ',forms `(,,@forms)))
#-swank (declare (ignore forms)))
(defmacro define-int-types (&rest bits)
`(progn
,@(mapcar (lambda (b) `(deftype ,(intern (format nil "UINT~A" b)) () '(unsigned-byte ,b))) bits)
,@(mapcar (lambda (b) `(deftype ,(intern (format nil "INT~A" b)) () '(signed-byte ,b))) bits)))
(define-int-types 2 4 7 8 15 16 31 32 62 63 64)
(declaim (inline println))
(defun println (obj &optional (stream *standard-output*))
(let ((*read-default-float-format* 'double-float))
(prog1 (princ obj stream) (terpri stream))))
(defconstant +mod+ 1000000007)
;;;
;;; Body
;;;
(defun preprocess ()
(let* ((n (read))
(intervals (make-array n :element-type 'list))
(dict (make-array n :element-type 'list))
(end 0)
(table (make-hash-table :size n :test #'equal)))
(dotimes (index n)
(let ((l (read-fixnum))
(r (read-fixnum)))
(unless (gethash (cons l r) table)
(setf (gethash (cons l r) table) t
(aref intervals end) (list* l r index)
(aref dict index) (aref intervals end)
end (+ end 1)))))
(values end
(adjust-array intervals end)
dict)))
(defun main ()
(declare #.OPT)
(multiple-value-bind (n intervals dict) (preprocess)
(declare ((simple-array list (*)) intervals dict)
(uint32 n))
(setq intervals (sort intervals #'> :key (lambda (x) (the uint32 (car x)))))
(let ((schedule (make-array n :element-type 'list))
(prev-l 1000000)
(end 0))
(loop for interval across intervals
for (l r) of-type (uint32 uint32) = interval
when (<= r prev-l)
do (setf (aref schedule end) interval
end (+ end 1)
prev-l l))
(println end)
(setq intervals (sort intervals
(lambda (x y)
(or (< (the uint32 (first x))
(the uint32 (first y)))
(and (= (the uint32 (first x))
(the uint32 (first y)))
(< (the uint32 (second x))
(the uint32 (second y))))))))
(gc)
(let ((schedule (nreverse (subseq schedule 0 end)))
(rtree (make-range-tree intervals
:xkey #'car
:ykey #'cadr
:value-key #'cddr))
(prev-r 0)
(init t))
(labels ((write* (x)
(if init
(setq init nil)
(write-char #\ ))
(write x)))
(with-buffered-stdout
(loop for i from 1 below end
for (next-l next-r) = (aref schedule i)
for argmin = (rt-query rtree prev-r prev-r (+ 1 next-l) (+ 1 next-l))
for (min-l min-r) = (aref dict argmin)
do (write* (+ argmin 1))
(dbg min-l min-r argmin)
(setq prev-r min-r)
finally (let ((argmin (rt-query rtree prev-r prev-r nil nil)))
(write* (+ argmin 1))))
(terpri)))))))
#-swank (main)
;;;
;;; Test and benchmark
;;;
#+swank
(defun io-equal (in-string out-string &key (function #'main) (test #'equal))
"Passes IN-STRING to *STANDARD-INPUT*, executes FUNCTION, and returns true if
the string output to *STANDARD-OUTPUT* is equal to OUT-STRING."
(labels ((ensure-last-lf (s)
(if (eql (uiop:last-char s) #\Linefeed)
s
(uiop:strcat s uiop:+lf+))))
(funcall test
(ensure-last-lf out-string)
(with-output-to-string (out)
(let ((*standard-output* out))
(with-input-from-string (*standard-input* (ensure-last-lf in-string))
(funcall function)))))))
#+swank
(defun get-clipbrd ()
(with-output-to-string (out)
;; (run-program "C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe" '("get-clipboard") :output out)
(run-program "powershell.exe" '("-Command" "Get-Clipboard") :output out :search t)))
#+swank (defparameter *this-pathname* (uiop:current-lisp-file-pathname))
#+swank (defparameter *dat-pathname* (uiop:merge-pathnames* "test.dat" *this-pathname*))
#+swank
(defun run (&optional thing (out *standard-output*))
"THING := null | string | symbol | pathname
null: run #'MAIN using the text on clipboard as input.
string: run #'MAIN using the string as input.
symbol: alias of FIVEAM:RUN!.
pathname: run #'MAIN using the text file as input."
(let ((*standard-output* out))
(etypecase thing
(null
(with-input-from-string (*standard-input* (delete #\Return (get-clipbrd)))
(main)))
(string
(with-input-from-string (*standard-input* (delete #\Return thing))
(main)))
(symbol (5am:run! thing))
(pathname
(with-open-file (*standard-input* thing)
(main))))))
#+swank
(defun gen-dat ()
(uiop:with-output-file (out *dat-pathname* :if-exists :supersede)
(format out "100000~%")
(dotimes (i 100000)
(let ((a (random 1000))
(b (random 1000)))
(when (> a b)
(rotatef a b))
(when (= a b)
(incf b))
(format out "~D ~D~%" a b)))))
#+swank
(defun bench (&optional (out (make-broadcast-stream)))
(time (run *dat-pathname* out)))
;; To run: (5am:run! :sample)
#+swank
(it.bese.fiveam:test :sample
(it.bese.fiveam:is
(common-lisp-user::io-equal "4
0 5
0 3
3 7
5 10
"
"2
1 4
"))
(it.bese.fiveam:is
(common-lisp-user::io-equal "5
0 5
0 3
3 7
5 10
7 12
"
"3
2 3 5
"))
(it.bese.fiveam:is
(common-lisp-user::io-equal "8
1 5
3 9
2 5
1 2
8 10
9 11
7 15
10 14
"
"4
4 3 5 8
")))
Submission Info
| Submission Time |
|
| Task |
C - 仕事計画 |
| User |
sansaqua |
| Language |
Common Lisp (SBCL 1.1.14) |
| Score |
100 |
| Code Size |
18482 Byte |
| Status |
AC |
| Exec Time |
582 ms |
| Memory |
228448 KiB |
Judge Result
| Set Name |
Sample |
All |
| Score / Max Score |
0 / 0 |
100 / 100 |
| Status |
|
|
| Set Name |
Test Cases |
| Sample |
sample1.txt, sample2.txt, sample3.txt |
| All |
0.txt, 1.txt, 10.txt, 11.txt, 12.txt, 13.txt, 14.txt, 15.txt, 16.txt, 17.txt, 18.txt, 19.txt, 2.txt, 20.txt, 21.txt, 22.txt, 23.txt, 3.txt, 4.txt, 5.txt, 6.txt, 7.txt, 8.txt, 9.txt, sample1.txt, sample2.txt, sample3.txt |
| Case Name |
Status |
Exec Time |
Memory |
| 0.txt |
AC |
141 ms |
31332 KiB |
| 1.txt |
AC |
141 ms |
31336 KiB |
| 10.txt |
AC |
141 ms |
31336 KiB |
| 11.txt |
AC |
141 ms |
31332 KiB |
| 12.txt |
AC |
142 ms |
31332 KiB |
| 13.txt |
AC |
145 ms |
31332 KiB |
| 14.txt |
AC |
143 ms |
31332 KiB |
| 15.txt |
AC |
142 ms |
31332 KiB |
| 16.txt |
AC |
143 ms |
31332 KiB |
| 17.txt |
AC |
143 ms |
31328 KiB |
| 18.txt |
AC |
162 ms |
39524 KiB |
| 19.txt |
AC |
499 ms |
228068 KiB |
| 2.txt |
AC |
142 ms |
31332 KiB |
| 20.txt |
AC |
521 ms |
228068 KiB |
| 21.txt |
AC |
161 ms |
39524 KiB |
| 22.txt |
AC |
185 ms |
39524 KiB |
| 23.txt |
AC |
582 ms |
228448 KiB |
| 3.txt |
AC |
142 ms |
31332 KiB |
| 4.txt |
AC |
142 ms |
31332 KiB |
| 5.txt |
AC |
142 ms |
31328 KiB |
| 6.txt |
AC |
141 ms |
31328 KiB |
| 7.txt |
AC |
141 ms |
31328 KiB |
| 8.txt |
AC |
141 ms |
31332 KiB |
| 9.txt |
AC |
143 ms |
31328 KiB |
| sample1.txt |
AC |
141 ms |
31328 KiB |
| sample2.txt |
AC |
142 ms |
31332 KiB |
| sample3.txt |
AC |
142 ms |
31332 KiB |