diff --git a/404.html b/404.html new file mode 100644 index 0000000..51eeeb8 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ + 404: Page not found | Monash Code Binder
Home 404: Page not found
404: Page not found
Cancel

404: Page not found

Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.

Head back Home to try finding it again, or search for it on the Archives page.

diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..430e8e5 --- /dev/null +++ b/about/index.html @@ -0,0 +1 @@ + About | Monash Code Binder
Home About
About
Cancel

About

This site is a collection of problem solving techniques, with related problems and implementations in Python + CPP. It also houses a printable binder for use in contests (COMING SOON).

If you’d like to contribute, please let me know (on Discord, jackson#5358) and I can add you to the project.

Any and all recommendations for particular problems or algorithms is also appreciated.

diff --git a/app.js b/app.js new file mode 100644 index 0000000..b2ab751 --- /dev/null +++ b/app.js @@ -0,0 +1 @@ +/* Registering Service Worker */ if('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); }; diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..cd8541a --- /dev/null +++ b/archives/index.html @@ -0,0 +1 @@ + Archives | Monash Code Binder
Home Archives
Archives
Cancel
diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..4c89596 --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,9 @@ +/*! + * The styles for Jekyll theme Chirpy + * + * Chirpy v3.3.2 (https://github.com/cotes2020/jekyll-theme-chirpy) + * © 2019 Cotes Chung + * MIT Licensed + */@import"https://fonts.googleapis.com/css2?family=Lato&family=Source+Sans+Pro:wght@400;600;900&display=swap";#search-results a,h5,h4,h3,h2,h1{color:var(--heading-color);font-weight:400;font-family:"Lato","Microsoft Yahei",sans-serif}.post-tag:hover,.tag:hover{background:var(--tag-hover);transition:background .35s ease-in-out}.table-wrapper>table tbody tr td,.table-wrapper>table thead th{padding:.4rem 1rem;font-size:95%;white-space:nowrap}#page-category a:hover,#page-tag a:hover,.license-wrapper>a:hover,#post-list .post-preview a:hover,#search-results a:hover,#topbar #breadcrumb a:hover,.post-content a:not(.img-hyperlink):hover,.post-meta a:hover,.post a:hover code,#access-lastmod a:hover,footer a:hover{color:#d2603a !important;border-bottom:1px solid #d2603a;text-decoration:none}#search-results a,.post-content a:not(.img-hyperlink),.post-meta a,a{color:var(--link-color)}.post-content a:not(.img-hyperlink),.post-meta a{border-bottom:1px solid var(--link-underline-color)}#page-category ul>li>a,#page-tag ul>li>a,#page .categories a:not(:hover),#page #tags a:not(:hover),#page #archives a:not(:hover),#search-results a,#access-lastmod a{border-bottom:none}#post-wrapper h5,#post-wrapper h4,#post-wrapper h3,#post-wrapper h2{line-height:1.2;margin-bottom:1rem}h5,h4,h3,h2{padding-top:3.5rem;margin-top:-2.5rem}html:not([mode]),html[mode=light]{--highlight-bg-color: #f7f7f7;--highlighter-rouge-color: #505050;--highlight-lineno-color: #c2c6cc;--highlight-lineno-border-color: #e9ecef;--inline-code-bg: #f3f3f3}html:not([mode]) .highlight .hll,html[mode=light] .highlight .hll{background-color:#ffc}html:not([mode]) .highlight .c,html[mode=light] .highlight .c{color:#998;font-style:italic}html:not([mode]) .highlight .err,html[mode=light] .highlight .err{color:#a61717;background-color:#e3d2d2}html:not([mode]) .highlight .k,html[mode=light] .highlight .k{color:#000;font-weight:bold}html:not([mode]) .highlight .o,html[mode=light] .highlight .o{color:#000;font-weight:bold}html:not([mode]) .highlight .cm,html[mode=light] .highlight .cm{color:#998;font-style:italic}html:not([mode]) .highlight .cp,html[mode=light] .highlight .cp{color:#999;font-weight:bold;font-style:italic}html:not([mode]) .highlight .c1,html[mode=light] .highlight .c1{color:#998;font-style:italic}html:not([mode]) .highlight .cs,html[mode=light] .highlight .cs{color:#999;font-weight:bold;font-style:italic}html:not([mode]) .highlight .gd,html[mode=light] .highlight .gd{color:#d01040;background-color:#fdd}html:not([mode]) .highlight .ge,html[mode=light] .highlight .ge{color:#000;font-style:italic}html:not([mode]) .highlight .gr,html[mode=light] .highlight .gr{color:#a00}html:not([mode]) .highlight .gh,html[mode=light] .highlight .gh{color:#999}html:not([mode]) .highlight .gi,html[mode=light] .highlight .gi{color:teal;background-color:#dfd}html:not([mode]) .highlight .go,html[mode=light] .highlight .go{color:#888}html:not([mode]) .highlight .gp,html[mode=light] .highlight .gp{color:#555}html:not([mode]) .highlight .gs,html[mode=light] .highlight .gs{font-weight:bold}html:not([mode]) .highlight .gu,html[mode=light] .highlight .gu{color:#aaa}html:not([mode]) .highlight .gt,html[mode=light] .highlight .gt{color:#a00}html:not([mode]) .highlight .kc,html[mode=light] .highlight .kc{color:#000;font-weight:bold}html:not([mode]) .highlight .kd,html[mode=light] .highlight .kd{color:#000;font-weight:bold}html:not([mode]) .highlight .kn,html[mode=light] .highlight .kn{color:#000;font-weight:bold}html:not([mode]) .highlight .kp,html[mode=light] .highlight .kp{color:#000;font-weight:bold}html:not([mode]) .highlight .kr,html[mode=light] .highlight .kr{color:#000;font-weight:bold}html:not([mode]) .highlight .kt,html[mode=light] .highlight .kt{color:#458;font-weight:bold}html:not([mode]) .highlight .m,html[mode=light] .highlight .m{color:#099}html:not([mode]) .highlight .s,html[mode=light] .highlight .s{color:#d01040}html:not([mode]) .highlight .na,html[mode=light] .highlight .na{color:teal}html:not([mode]) .highlight .nb,html[mode=light] .highlight .nb{color:#0086b3}html:not([mode]) .highlight .nc,html[mode=light] .highlight .nc{color:#458;font-weight:bold}html:not([mode]) .highlight .no,html[mode=light] .highlight .no{color:teal}html:not([mode]) .highlight .nd,html[mode=light] .highlight .nd{color:#3c5d5d;font-weight:bold}html:not([mode]) .highlight .ni,html[mode=light] .highlight .ni{color:purple}html:not([mode]) .highlight .ne,html[mode=light] .highlight .ne{color:#900;font-weight:bold}html:not([mode]) .highlight .nf,html[mode=light] .highlight .nf{color:#900;font-weight:bold}html:not([mode]) .highlight .nl,html[mode=light] .highlight .nl{color:#900;font-weight:bold}html:not([mode]) .highlight .nn,html[mode=light] .highlight .nn{color:#555}html:not([mode]) .highlight .nt,html[mode=light] .highlight .nt{color:navy}html:not([mode]) .highlight .nv,html[mode=light] .highlight .nv{color:teal}html:not([mode]) .highlight .ow,html[mode=light] .highlight .ow{color:#000;font-weight:bold}html:not([mode]) .highlight .w,html[mode=light] .highlight .w{color:#bbb}html:not([mode]) .highlight .mf,html[mode=light] .highlight .mf{color:#099}html:not([mode]) .highlight .mh,html[mode=light] .highlight .mh{color:#099}html:not([mode]) .highlight .mi,html[mode=light] .highlight .mi{color:#099}html:not([mode]) .highlight .mo,html[mode=light] .highlight .mo{color:#099}html:not([mode]) .highlight .sb,html[mode=light] .highlight .sb{color:#d01040}html:not([mode]) .highlight .sc,html[mode=light] .highlight .sc{color:#d01040}html:not([mode]) .highlight .sd,html[mode=light] .highlight .sd{color:#d01040}html:not([mode]) .highlight .s2,html[mode=light] .highlight .s2{color:#d01040}html:not([mode]) .highlight .se,html[mode=light] .highlight .se{color:#d01040}html:not([mode]) .highlight .sh,html[mode=light] .highlight .sh{color:#d01040}html:not([mode]) .highlight .si,html[mode=light] .highlight .si{color:#d01040}html:not([mode]) .highlight .sx,html[mode=light] .highlight .sx{color:#d01040}html:not([mode]) .highlight .sr,html[mode=light] .highlight .sr{color:#009926}html:not([mode]) .highlight .s1,html[mode=light] .highlight .s1{color:#d01040}html:not([mode]) .highlight .ss,html[mode=light] .highlight .ss{color:#990073}html:not([mode]) .highlight .bp,html[mode=light] .highlight .bp{color:#999}html:not([mode]) .highlight .vc,html[mode=light] .highlight .vc{color:teal}html:not([mode]) .highlight .vg,html[mode=light] .highlight .vg{color:teal}html:not([mode]) .highlight .vi,html[mode=light] .highlight .vi{color:teal}html:not([mode]) .highlight .il,html[mode=light] .highlight .il{color:#099}html[mode=dark]{--highlight-bg-color: #252525;--highlighter-rouge-color: #de6b18;--highlight-lineno-color: #6c6c6d;--highlight-lineno-border-color: #303435;--inline-code-bg: #272822}html[mode=dark] .highlight .gp{color:#818c96}html[mode=dark] pre{color:#bfbfbf}html[mode=dark] kbd{background-color:#000}html[mode=dark] .highlight pre{background-color:var(--highlight-bg-color)}html[mode=dark] .highlight .hll{background-color:var(--highlight-bg-color)}html[mode=dark] .highlight .c{color:#75715e}html[mode=dark] .highlight .err{color:#960050;background-color:#1e0010}html[mode=dark] .highlight .k{color:#66d9ef}html[mode=dark] .highlight .l{color:#ae81ff}html[mode=dark] .highlight .n{color:#f8f8f2}html[mode=dark] .highlight .o{color:#f92672}html[mode=dark] .highlight .p{color:#f8f8f2}html[mode=dark] .highlight .cm{color:#75715e}html[mode=dark] .highlight .cp{color:#75715e}html[mode=dark] .highlight .c1{color:#75715e}html[mode=dark] .highlight .cs{color:#75715e}html[mode=dark] .highlight .ge{color:inherit;font-style:italic}html[mode=dark] .highlight .gs{font-weight:bold}html[mode=dark] .highlight .kc{color:#66d9ef}html[mode=dark] .highlight .kd{color:#66d9ef}html[mode=dark] .highlight .kn{color:#f92672}html[mode=dark] .highlight .kp{color:#66d9ef}html[mode=dark] .highlight .kr{color:#66d9ef}html[mode=dark] .highlight .kt{color:#66d9ef}html[mode=dark] .highlight .ld{color:#e6db74}html[mode=dark] .highlight .m{color:#ae81ff}html[mode=dark] .highlight .s{color:#e6db74}html[mode=dark] .highlight .na{color:#a6e22e}html[mode=dark] .highlight .nb{color:#f8f8f2}html[mode=dark] .highlight .nc{color:#a6e22e}html[mode=dark] .highlight .no{color:#66d9ef}html[mode=dark] .highlight .nd{color:#a6e22e}html[mode=dark] .highlight .ni{color:#f8f8f2}html[mode=dark] .highlight .ne{color:#a6e22e}html[mode=dark] .highlight .nf{color:#a6e22e}html[mode=dark] .highlight .nl{color:#f8f8f2}html[mode=dark] .highlight .nn{color:#f8f8f2}html[mode=dark] .highlight .nx{color:#a6e22e}html[mode=dark] .highlight .py{color:#f8f8f2}html[mode=dark] .highlight .nt{color:#f92672}html[mode=dark] .highlight .nv{color:#f8f8f2}html[mode=dark] .highlight .ow{color:#f92672}html[mode=dark] .highlight .w{color:#f8f8f2}html[mode=dark] .highlight .mf{color:#ae81ff}html[mode=dark] .highlight .mh{color:#ae81ff}html[mode=dark] .highlight .mi{color:#ae81ff}html[mode=dark] .highlight .mo{color:#ae81ff}html[mode=dark] .highlight .sb{color:#e6db74}html[mode=dark] .highlight .sc{color:#e6db74}html[mode=dark] .highlight .sd{color:#e6db74}html[mode=dark] .highlight .s2{color:#e6db74}html[mode=dark] .highlight .se{color:#ae81ff}html[mode=dark] .highlight .sh{color:#e6db74}html[mode=dark] .highlight .si{color:#e6db74}html[mode=dark] .highlight .sx{color:#e6db74}html[mode=dark] .highlight .sr{color:#e6db74}html[mode=dark] .highlight .s1{color:#e6db74}html[mode=dark] .highlight .ss{color:#e6db74}html[mode=dark] .highlight .bp{color:#f8f8f2}html[mode=dark] .highlight .vc{color:#f8f8f2}html[mode=dark] .highlight .vg{color:#f8f8f2}html[mode=dark] .highlight .vi{color:#f8f8f2}html[mode=dark] .highlight .il{color:#ae81ff}html[mode=dark] .highlight .gu{color:#75715e}html[mode=dark] .highlight .gd{color:#f92672;background-color:#561c08}html[mode=dark] .highlight .gi{color:#a6e22e;background-color:#0b5858}@media(prefers-color-scheme: dark){html:not([mode]),html[mode=dark]{--highlight-bg-color: #252525;--highlighter-rouge-color: #de6b18;--highlight-lineno-color: #6c6c6d;--highlight-lineno-border-color: #303435;--inline-code-bg: #272822}html:not([mode]) .highlight .gp,html[mode=dark] .highlight .gp{color:#818c96}html:not([mode]) pre,html[mode=dark] pre{color:#bfbfbf}html:not([mode]) kbd,html[mode=dark] kbd{background-color:#000}html:not([mode]) .highlight pre,html[mode=dark] .highlight pre{background-color:var(--highlight-bg-color)}html:not([mode]) .highlight .hll,html[mode=dark] .highlight .hll{background-color:var(--highlight-bg-color)}html:not([mode]) .highlight .c,html[mode=dark] .highlight .c{color:#75715e}html:not([mode]) .highlight .err,html[mode=dark] .highlight .err{color:#960050;background-color:#1e0010}html:not([mode]) .highlight .k,html[mode=dark] .highlight .k{color:#66d9ef}html:not([mode]) .highlight .l,html[mode=dark] .highlight .l{color:#ae81ff}html:not([mode]) .highlight .n,html[mode=dark] .highlight .n{color:#f8f8f2}html:not([mode]) .highlight .o,html[mode=dark] .highlight .o{color:#f92672}html:not([mode]) .highlight .p,html[mode=dark] .highlight .p{color:#f8f8f2}html:not([mode]) .highlight .cm,html[mode=dark] .highlight .cm{color:#75715e}html:not([mode]) .highlight .cp,html[mode=dark] .highlight .cp{color:#75715e}html:not([mode]) .highlight .c1,html[mode=dark] .highlight .c1{color:#75715e}html:not([mode]) .highlight .cs,html[mode=dark] .highlight .cs{color:#75715e}html:not([mode]) .highlight .ge,html[mode=dark] .highlight .ge{color:inherit;font-style:italic}html:not([mode]) .highlight .gs,html[mode=dark] .highlight .gs{font-weight:bold}html:not([mode]) .highlight .kc,html[mode=dark] .highlight .kc{color:#66d9ef}html:not([mode]) .highlight .kd,html[mode=dark] .highlight .kd{color:#66d9ef}html:not([mode]) .highlight .kn,html[mode=dark] .highlight .kn{color:#f92672}html:not([mode]) .highlight .kp,html[mode=dark] .highlight .kp{color:#66d9ef}html:not([mode]) .highlight .kr,html[mode=dark] .highlight .kr{color:#66d9ef}html:not([mode]) .highlight .kt,html[mode=dark] .highlight .kt{color:#66d9ef}html:not([mode]) .highlight .ld,html[mode=dark] .highlight .ld{color:#e6db74}html:not([mode]) .highlight .m,html[mode=dark] .highlight .m{color:#ae81ff}html:not([mode]) .highlight .s,html[mode=dark] .highlight .s{color:#e6db74}html:not([mode]) .highlight .na,html[mode=dark] .highlight .na{color:#a6e22e}html:not([mode]) .highlight .nb,html[mode=dark] .highlight .nb{color:#f8f8f2}html:not([mode]) .highlight .nc,html[mode=dark] .highlight .nc{color:#a6e22e}html:not([mode]) .highlight .no,html[mode=dark] .highlight .no{color:#66d9ef}html:not([mode]) .highlight .nd,html[mode=dark] .highlight .nd{color:#a6e22e}html:not([mode]) .highlight .ni,html[mode=dark] .highlight .ni{color:#f8f8f2}html:not([mode]) .highlight .ne,html[mode=dark] .highlight .ne{color:#a6e22e}html:not([mode]) .highlight .nf,html[mode=dark] .highlight .nf{color:#a6e22e}html:not([mode]) .highlight .nl,html[mode=dark] .highlight .nl{color:#f8f8f2}html:not([mode]) .highlight .nn,html[mode=dark] .highlight .nn{color:#f8f8f2}html:not([mode]) .highlight .nx,html[mode=dark] .highlight .nx{color:#a6e22e}html:not([mode]) .highlight .py,html[mode=dark] .highlight .py{color:#f8f8f2}html:not([mode]) .highlight .nt,html[mode=dark] .highlight .nt{color:#f92672}html:not([mode]) .highlight .nv,html[mode=dark] .highlight .nv{color:#f8f8f2}html:not([mode]) .highlight .ow,html[mode=dark] .highlight .ow{color:#f92672}html:not([mode]) .highlight .w,html[mode=dark] .highlight .w{color:#f8f8f2}html:not([mode]) .highlight .mf,html[mode=dark] .highlight .mf{color:#ae81ff}html:not([mode]) .highlight .mh,html[mode=dark] .highlight .mh{color:#ae81ff}html:not([mode]) .highlight .mi,html[mode=dark] .highlight .mi{color:#ae81ff}html:not([mode]) .highlight .mo,html[mode=dark] .highlight .mo{color:#ae81ff}html:not([mode]) .highlight .sb,html[mode=dark] .highlight .sb{color:#e6db74}html:not([mode]) .highlight .sc,html[mode=dark] .highlight .sc{color:#e6db74}html:not([mode]) .highlight .sd,html[mode=dark] .highlight .sd{color:#e6db74}html:not([mode]) .highlight .s2,html[mode=dark] .highlight .s2{color:#e6db74}html:not([mode]) .highlight .se,html[mode=dark] .highlight .se{color:#ae81ff}html:not([mode]) .highlight .sh,html[mode=dark] .highlight .sh{color:#e6db74}html:not([mode]) .highlight .si,html[mode=dark] .highlight .si{color:#e6db74}html:not([mode]) .highlight .sx,html[mode=dark] .highlight .sx{color:#e6db74}html:not([mode]) .highlight .sr,html[mode=dark] .highlight .sr{color:#e6db74}html:not([mode]) .highlight .s1,html[mode=dark] .highlight .s1{color:#e6db74}html:not([mode]) .highlight .ss,html[mode=dark] .highlight .ss{color:#e6db74}html:not([mode]) .highlight .bp,html[mode=dark] .highlight .bp{color:#f8f8f2}html:not([mode]) .highlight .vc,html[mode=dark] .highlight .vc{color:#f8f8f2}html:not([mode]) .highlight .vg,html[mode=dark] .highlight .vg{color:#f8f8f2}html:not([mode]) .highlight .vi,html[mode=dark] .highlight .vi{color:#f8f8f2}html:not([mode]) .highlight .il,html[mode=dark] .highlight .il{color:#ae81ff}html:not([mode]) .highlight .gu,html[mode=dark] .highlight .gu{color:#75715e}html:not([mode]) .highlight .gd,html[mode=dark] .highlight .gd{color:#f92672;background-color:#561c08}html:not([mode]) .highlight .gi,html[mode=dark] .highlight .gi{color:#a6e22e;background-color:#0b5858}html[mode=light]{--highlight-bg-color: #f7f7f7;--highlighter-rouge-color: #505050;--highlight-lineno-color: #c2c6cc;--highlight-lineno-border-color: #e9ecef;--inline-code-bg: #f3f3f3}html[mode=light] .highlight .hll{background-color:#ffc}html[mode=light] .highlight .c{color:#998;font-style:italic}html[mode=light] .highlight .err{color:#a61717;background-color:#e3d2d2}html[mode=light] .highlight .k{color:#000;font-weight:bold}html[mode=light] .highlight .o{color:#000;font-weight:bold}html[mode=light] .highlight .cm{color:#998;font-style:italic}html[mode=light] .highlight .cp{color:#999;font-weight:bold;font-style:italic}html[mode=light] .highlight .c1{color:#998;font-style:italic}html[mode=light] .highlight .cs{color:#999;font-weight:bold;font-style:italic}html[mode=light] .highlight .gd{color:#d01040;background-color:#fdd}html[mode=light] .highlight .ge{color:#000;font-style:italic}html[mode=light] .highlight .gr{color:#a00}html[mode=light] .highlight .gh{color:#999}html[mode=light] .highlight .gi{color:teal;background-color:#dfd}html[mode=light] .highlight .go{color:#888}html[mode=light] .highlight .gp{color:#555}html[mode=light] .highlight .gs{font-weight:bold}html[mode=light] .highlight .gu{color:#aaa}html[mode=light] .highlight .gt{color:#a00}html[mode=light] .highlight .kc{color:#000;font-weight:bold}html[mode=light] .highlight .kd{color:#000;font-weight:bold}html[mode=light] .highlight .kn{color:#000;font-weight:bold}html[mode=light] .highlight .kp{color:#000;font-weight:bold}html[mode=light] .highlight .kr{color:#000;font-weight:bold}html[mode=light] .highlight .kt{color:#458;font-weight:bold}html[mode=light] .highlight .m{color:#099}html[mode=light] .highlight .s{color:#d01040}html[mode=light] .highlight .na{color:teal}html[mode=light] .highlight .nb{color:#0086b3}html[mode=light] .highlight .nc{color:#458;font-weight:bold}html[mode=light] .highlight .no{color:teal}html[mode=light] .highlight .nd{color:#3c5d5d;font-weight:bold}html[mode=light] .highlight .ni{color:purple}html[mode=light] .highlight .ne{color:#900;font-weight:bold}html[mode=light] .highlight .nf{color:#900;font-weight:bold}html[mode=light] .highlight .nl{color:#900;font-weight:bold}html[mode=light] .highlight .nn{color:#555}html[mode=light] .highlight .nt{color:navy}html[mode=light] .highlight .nv{color:teal}html[mode=light] .highlight .ow{color:#000;font-weight:bold}html[mode=light] .highlight .w{color:#bbb}html[mode=light] .highlight .mf{color:#099}html[mode=light] .highlight .mh{color:#099}html[mode=light] .highlight .mi{color:#099}html[mode=light] .highlight .mo{color:#099}html[mode=light] .highlight .sb{color:#d01040}html[mode=light] .highlight .sc{color:#d01040}html[mode=light] .highlight .sd{color:#d01040}html[mode=light] .highlight .s2{color:#d01040}html[mode=light] .highlight .se{color:#d01040}html[mode=light] .highlight .sh{color:#d01040}html[mode=light] .highlight .si{color:#d01040}html[mode=light] .highlight .sx{color:#d01040}html[mode=light] .highlight .sr{color:#009926}html[mode=light] .highlight .s1{color:#d01040}html[mode=light] .highlight .ss{color:#990073}html[mode=light] .highlight .bp{color:#999}html[mode=light] .highlight .vc{color:teal}html[mode=light] .highlight .vg{color:teal}html[mode=light] .highlight .vi{color:teal}html[mode=light] .highlight .il{color:#099}}figure.highlight,.highlight,.highlighter-rouge,div>pre{background:var(--highlight-bg-color)}.highlight,.highlighter-rouge,div>pre{border-radius:6px}div[class^=highlighter-rouge] td.rouge-code,div.language-plaintext.highlighter-rouge td.rouge-code,div.language-console.highlighter-rouge td.rouge-code,div.language-terminal.highlighter-rouge td.rouge-code,div>pre{padding:1.5rem}.highlighter-rouge{color:var(--highlighter-rouge-color);margin-top:.5rem;margin-bottom:1.2em}.highlight{overflow:auto}.highlight .lineno{margin-left:.2rem;padding-right:.5rem;min-width:2.2rem;text-align:right;color:var(--highlight-lineno-color);border-right:1px solid var(--highlight-lineno-border-color);-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.highlight pre{margin-bottom:0;font-size:.85rem;line-height:1.4rem;word-wrap:normal}.highlight table{padding:0;border:0}.highlight table td pre{overflow:visible;word-break:normal}.highlight td{padding:0;border:0}code{-webkit-hyphens:none;-ms-hyphens:none;-moz-hyphens:none;hyphens:none}code.highlighter-rouge{font-size:.85rem;padding:3px 5px;border-radius:4px;background-color:var(--inline-code-bg)}a>code.highlighter-rouge{padding-bottom:0;color:inherit}a:hover>code.highlighter-rouge{border-bottom:none}blockquote code.highlighter-rouge{color:inherit}td.rouge-code{padding:1.5rem 1.5rem 1.5rem 1rem}td.rouge-code a{color:inherit !important;border-bottom:none !important;pointer-events:none}div[class^=highlighter-rouge] pre.lineno,div.language-plaintext.highlighter-rouge pre.lineno,div.language-console.highlighter-rouge pre.lineno,div.language-terminal.highlighter-rouge pre.lineno{display:none}div[class^=language-]::before{content:attr(lang);position:absolute;right:2rem;margin-top:3px;font-size:.7rem;font-weight:600;color:var(--highlight-lineno-color);text-transform:uppercase}@media(min-width: 768px){div[class^=language-]::before{right:3.1rem}}@media(min-width: 1650px){div[class^=language-]::before{right:3.5rem}}html:not([mode]),html[mode=light]{--body-bg: #fafafa;--mask-bg: #c1c3c5;--main-wrapper-bg: white;--main-border-color: #f3f3f3;--btn-border-color: #e9ecef;--text-color: #4a4a4a;--heading-color: black;--blockquote-border-color: #eee;--blockquote-text-color: #9a9a9a;--link-color: #2a408e;--link-underline-color: #dee2e6;--text-muted-color: gray;--tb-odd-bg: #fbfcfd;--tb-border-color: #eaeaea;--button-bg: #fff;--btn-backtotop-color: #686868;--btn-backtotop-border-color: #f1f1f1;--btn-box-shadow: #eaeaea;--checkbox-color: #c5c5c5;--checkbox-checked-color: #07a8f7;--sidebar-bg: radial-gradient( circle, rgba(42, 30, 107, 1) 0%, rgba(35, 37, 46, 1) 100%);--nav-cursor-color: #fcfcfc;--topbar-wrapper-bg: white;--topbar-text-color: rgb(78, 78, 78);--search-wrapper-bg: #f5f5f5;--search-tag-bg: #f8f9fa;--search-icon-color: #c2c6cc;--input-focus-border-color: var(--btn-border-color);--post-list-text-color: dimgray;--btn-patinator-text-color: #555555;--btn-paginator-hover-color: #e9ecef;--btn-active-bg: #2a408e;--btn-active-border-color: #007bff;--btn-text-color: #f8f8f8;--btn-paginator-border-color: #f1f1f1;--btn-paginator-shadow: #4b92d2;--pin-bg: #f5f5f5;--pin-color: #999fa4;--btn-share-hover-color: var(--link-color);--card-border-color: #f1f1f1;--card-box-shadow: rgba(234, 234, 234, 0.7686274509803922);--label-color: #616161;--relate-post-date: rgba(30, 55, 70, 0.4);--tag-bg: rgba(0, 0, 0, 0.075);--tag-border: #dee2e6;--tag-shadow: var(--btn-border-color);--tag-hover: rgb(222, 226, 230);--categories-hover-bg: var(--btn-border-color);--dash-color: silver;--timeline-color: rgba(0, 0, 0, 0.075);--timeline-node-bg: #c2c6cc;--timeline-year-dot-color: #ffffff;--footer-bg-color: #ffffff;--footnote-target-bg: lightcyan;--footer-link: #424242}html:not([mode]) .mode-toggle,html[mode=light] .mode-toggle{transform:none}html[mode=dark]{--main-wrapper-bg: rgb(27, 27, 30);--body-bg: var(--main-wrapper-bg);--topbar-wrapper-bg: rgb(39, 40, 43);--search-wrapper-bg: rgb(34, 34, 39);--search-icon-color: rgb(100, 102, 105);--input-focus-border-color: rgb(112, 114, 115);--mask-bg: rgb(68, 69, 70);--footer-bg-color: var(--main-wrapper-bg);--text-color: rgb(175, 176, 177);--heading-color: #cccccc;--text-muted-color: rgb(107, 116, 124);--link-color: rgb(138, 180, 248);--link-underline-color: rgb(82, 108, 150);--main-border-color: rgb(44, 45, 45);--button-bg: rgb(39, 40, 43);--blockquote-border-color: rgb(66, 66, 66);--blockquote-text-color: rgb(117, 117, 117);--btn-border-color: rgb(63, 65, 68);--btn-backtotop-color: var(--text-color);--btn-backtotop-border-color: var(--btn-border-color);--btn-box-shadow: var(--main-wrapper-bg);--card-header-bg: rgb(51, 50, 50);--label-color: rgb(108, 117, 125);--checkbox-color: rgb(118 120 121);--checkbox-checked-color: var(--link-color);--nav-cursor-color: rgb(183, 182, 182);--sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);--topbar-text-color: var(--text-color);--post-list-text-color: rgb(175, 176, 177);--btn-patinator-text-color: var(--text-color);--btn-paginator-hover-color: rgb(64, 65, 66);--btn-active-bg: rgba(28, 52, 94, 1);--btn-active-border-color: rgb(66, 94, 138);--btn-text-color: var(--text-color);--btn-paginator-border-color: var(--btn-border-color);--btn-paginator-shadow: var(--main-wrapper-bg);--pin-bg: rgb(34 35 37);--pin-color: inherit;--toc-highlight: rgb(116, 178, 243);--tag-bg: rgb(41, 40, 40);--tag-hover: rgb(43, 56, 62);--tb-odd-bg: rgba(42, 47, 53, 0.52);--tb-even-bg: rgb(31, 31, 34);--tb-border-color: var(--tb-odd-bg);--footnote-target-bg: rgb(63, 81, 181);--btn-share-color: #6c757d;--btn-share-hover-color: #bfc1ca;--relate-post-date: var(--text-muted-color);--card-bg: rgb(39, 40, 43);--card-border-color: rgb(53, 53, 60);--card-box-shadow: var(--main-wrapper-bg);--tag-border: rgb(59, 79, 88);--tag-shadow: rgb(32, 33, 33);--search-tag-bg: var(--tag-bg);--dash-color: rgb(63, 65, 68);--categories-border: rgb(64, 66, 69);--categories-hover-bg: rgb(73, 75, 76);--timeline-node-bg: rgb(150, 152, 156);--timeline-color: rgb(63, 65, 68);--timeline-year-dot-color: var(--timeline-color);--footer-link: rgb(171, 171, 171)}html[mode=dark] .post-content img{filter:brightness(90%)}html[mode=dark] hr{border-color:var(--main-border-color)}html[mode=dark] nav[data-toggle=toc] .nav-link.active,html[mode=dark] nav[data-toggle=toc] .nav-link.active:focus,html[mode=dark] nav[data-toggle=toc] .nav-link.active:hover,html[mode=dark] nav[data-toggle=toc] .nav>li>a:focus,html[mode=dark] nav[data-toggle=toc] .nav>li>a:hover{color:var(--toc-highlight) !important;border-left-color:var(--toc-highlight) !important}html[mode=dark] .categories.card,html[mode=dark] .list-group-item{background-color:var(--card-bg)}html[mode=dark] .categories .card-header{background-color:var(--card-header-bg)}html[mode=dark] .categories .list-group-item{border-left:none;border-right:none;padding-left:2rem;border-color:var(--categories-border)}html[mode=dark] .categories .list-group-item:last-child{border-bottom-color:var(--card-bg)}html[mode=dark] #archives li:nth-child(odd){background-image:linear-gradient(to left, rgb(26, 26, 30), rgb(39, 39, 45), rgb(39, 39, 45), rgb(39, 39, 45), rgb(26, 26, 30))}html[mode=dark] .mode-toggle{transform:rotateY(180deg)}@media(prefers-color-scheme: dark){html:not([mode]),html[mode=dark]{--main-wrapper-bg: rgb(27, 27, 30);--body-bg: var(--main-wrapper-bg);--topbar-wrapper-bg: rgb(39, 40, 43);--search-wrapper-bg: rgb(34, 34, 39);--search-icon-color: rgb(100, 102, 105);--input-focus-border-color: rgb(112, 114, 115);--mask-bg: rgb(68, 69, 70);--footer-bg-color: var(--main-wrapper-bg);--text-color: rgb(175, 176, 177);--heading-color: #cccccc;--text-muted-color: rgb(107, 116, 124);--link-color: rgb(138, 180, 248);--link-underline-color: rgb(82, 108, 150);--main-border-color: rgb(44, 45, 45);--button-bg: rgb(39, 40, 43);--blockquote-border-color: rgb(66, 66, 66);--blockquote-text-color: rgb(117, 117, 117);--btn-border-color: rgb(63, 65, 68);--btn-backtotop-color: var(--text-color);--btn-backtotop-border-color: var(--btn-border-color);--btn-box-shadow: var(--main-wrapper-bg);--card-header-bg: rgb(51, 50, 50);--label-color: rgb(108, 117, 125);--checkbox-color: rgb(118 120 121);--checkbox-checked-color: var(--link-color);--nav-cursor-color: rgb(183, 182, 182);--sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);--topbar-text-color: var(--text-color);--post-list-text-color: rgb(175, 176, 177);--btn-patinator-text-color: var(--text-color);--btn-paginator-hover-color: rgb(64, 65, 66);--btn-active-bg: rgba(28, 52, 94, 1);--btn-active-border-color: rgb(66, 94, 138);--btn-text-color: var(--text-color);--btn-paginator-border-color: var(--btn-border-color);--btn-paginator-shadow: var(--main-wrapper-bg);--pin-bg: rgb(34 35 37);--pin-color: inherit;--toc-highlight: rgb(116, 178, 243);--tag-bg: rgb(41, 40, 40);--tag-hover: rgb(43, 56, 62);--tb-odd-bg: rgba(42, 47, 53, 0.52);--tb-even-bg: rgb(31, 31, 34);--tb-border-color: var(--tb-odd-bg);--footnote-target-bg: rgb(63, 81, 181);--btn-share-color: #6c757d;--btn-share-hover-color: #bfc1ca;--relate-post-date: var(--text-muted-color);--card-bg: rgb(39, 40, 43);--card-border-color: rgb(53, 53, 60);--card-box-shadow: var(--main-wrapper-bg);--tag-border: rgb(59, 79, 88);--tag-shadow: rgb(32, 33, 33);--search-tag-bg: var(--tag-bg);--dash-color: rgb(63, 65, 68);--categories-border: rgb(64, 66, 69);--categories-hover-bg: rgb(73, 75, 76);--timeline-node-bg: rgb(150, 152, 156);--timeline-color: rgb(63, 65, 68);--timeline-year-dot-color: var(--timeline-color);--footer-link: rgb(171, 171, 171)}html:not([mode]) .post-content img,html[mode=dark] .post-content img{filter:brightness(90%)}html:not([mode]) hr,html[mode=dark] hr{border-color:var(--main-border-color)}html:not([mode]) nav[data-toggle=toc] .nav-link.active,html:not([mode]) nav[data-toggle=toc] .nav-link.active:focus,html:not([mode]) nav[data-toggle=toc] .nav-link.active:hover,html:not([mode]) nav[data-toggle=toc] .nav>li>a:focus,html:not([mode]) nav[data-toggle=toc] .nav>li>a:hover,html[mode=dark] nav[data-toggle=toc] .nav-link.active,html[mode=dark] nav[data-toggle=toc] .nav-link.active:focus,html[mode=dark] nav[data-toggle=toc] .nav-link.active:hover,html[mode=dark] nav[data-toggle=toc] .nav>li>a:focus,html[mode=dark] nav[data-toggle=toc] .nav>li>a:hover{color:var(--toc-highlight) !important;border-left-color:var(--toc-highlight) !important}html:not([mode]) .categories.card,html:not([mode]) .list-group-item,html[mode=dark] .categories.card,html[mode=dark] .list-group-item{background-color:var(--card-bg)}html:not([mode]) .categories .card-header,html[mode=dark] .categories .card-header{background-color:var(--card-header-bg)}html:not([mode]) .categories .list-group-item,html[mode=dark] .categories .list-group-item{border-left:none;border-right:none;padding-left:2rem;border-color:var(--categories-border)}html:not([mode]) .categories .list-group-item:last-child,html[mode=dark] .categories .list-group-item:last-child{border-bottom-color:var(--card-bg)}html:not([mode]) #archives li:nth-child(odd),html[mode=dark] #archives li:nth-child(odd){background-image:linear-gradient(to left, rgb(26, 26, 30), rgb(39, 39, 45), rgb(39, 39, 45), rgb(39, 39, 45), rgb(26, 26, 30))}html:not([mode]) .mode-toggle,html[mode=dark] .mode-toggle{transform:rotateY(180deg)}html[mode=light]{--body-bg: #fafafa;--mask-bg: #c1c3c5;--main-wrapper-bg: white;--main-border-color: #f3f3f3;--btn-border-color: #e9ecef;--text-color: #4a4a4a;--heading-color: black;--blockquote-border-color: #eee;--blockquote-text-color: #9a9a9a;--link-color: #2a408e;--link-underline-color: #dee2e6;--text-muted-color: gray;--tb-odd-bg: #fbfcfd;--tb-border-color: #eaeaea;--button-bg: #fff;--btn-backtotop-color: #686868;--btn-backtotop-border-color: #f1f1f1;--btn-box-shadow: #eaeaea;--checkbox-color: #c5c5c5;--checkbox-checked-color: #07a8f7;--sidebar-bg: radial-gradient( circle, rgba(42, 30, 107, 1) 0%, rgba(35, 37, 46, 1) 100%);--nav-cursor-color: #fcfcfc;--topbar-wrapper-bg: white;--topbar-text-color: rgb(78, 78, 78);--search-wrapper-bg: #f5f5f5;--search-tag-bg: #f8f9fa;--search-icon-color: #c2c6cc;--input-focus-border-color: var(--btn-border-color);--post-list-text-color: dimgray;--btn-patinator-text-color: #555555;--btn-paginator-hover-color: #e9ecef;--btn-active-bg: #2a408e;--btn-active-border-color: #007bff;--btn-text-color: #f8f8f8;--btn-paginator-border-color: #f1f1f1;--btn-paginator-shadow: #4b92d2;--pin-bg: #f5f5f5;--pin-color: #999fa4;--btn-share-hover-color: var(--link-color);--card-border-color: #f1f1f1;--card-box-shadow: rgba(234, 234, 234, 0.7686274509803922);--label-color: #616161;--relate-post-date: rgba(30, 55, 70, 0.4);--tag-bg: rgba(0, 0, 0, 0.075);--tag-border: #dee2e6;--tag-shadow: var(--btn-border-color);--tag-hover: rgb(222, 226, 230);--categories-hover-bg: var(--btn-border-color);--dash-color: silver;--timeline-color: rgba(0, 0, 0, 0.075);--timeline-node-bg: #c2c6cc;--timeline-year-dot-color: #ffffff;--footer-bg-color: #ffffff;--footnote-target-bg: lightcyan;--footer-link: #424242}html[mode=light] .mode-toggle{transform:none}}:root{font-size:16px}body{line-height:1.75rem;background:var(--body-bg);color:var(--text-color);-webkit-font-smoothing:antialiased;font-family:"Source Sans Pro","Microsoft Yahei",sans-serif}h1{font-size:1.8rem}h2{font-size:1.4rem}h3{font-size:1.25rem}h4{font-size:1.15rem}h5{font-size:1.1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:1rem}img{max-width:100%}blockquote{border-left:5px solid var(--blockquote-border-color);padding-left:1rem;color:var(--blockquote-text-color)}kbd{margin:0 .3rem}footer{position:absolute;bottom:0;padding:0 1rem;height:5rem;font-size:.8rem;color:#7a7b7d;background-color:var(--footer-bg-color)}footer>div.d-flex{line-height:1.2rem;width:95%;max-width:1045px;border-top:1px solid var(--main-border-color);margin-bottom:1rem}footer>div.d-flex>div{width:350px}footer a{color:var(--footer-link)}footer a:link{text-decoration:none}footer a:hover{text-decoration:none}footer .footer-right{text-align:right}.access{top:2rem;transition:top .2s ease-in-out;margin-right:1.5rem;margin-top:3rem;margin-bottom:4rem}.access:only-child{position:-webkit-sticky;position:sticky}.access.topbar-down{top:6rem}.access>div{padding-left:1rem;border-left:1px solid var(--main-border-color)}.access>div:not(:last-child){margin-bottom:4rem}.access span{color:var(--label-color);font-size:inherit;font-weight:600;display:block;line-height:1.2;padding-top:.5rem;padding-bottom:.5rem;margin-top:0;margin-bottom:0;letter-spacing:-0.02em}.access .post-content{font-size:.9rem}#access-tags>div.post-content>div{max-width:80%}#access-tags .post-tag{display:inline-block;line-height:1rem;font-size:.85rem;background:none;border:1px solid var(--btn-border-color);border-radius:.8rem;padding:.3rem .5rem;margin:0 .35rem .5rem 0}#access-tags .post-tag:hover{background-color:#2a408e;border-color:#2a408e;color:#fff;transition:none}#access-lastmod li{height:1.8rem;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;list-style:none}#access-lastmod a{color:#6c757d}.footnotes>ol{padding-left:2rem;margin-top:.5rem}.footnotes>ol>li:not(:last-child){margin-bottom:.3rem}.footnotes>ol>li>p{margin-left:.25em;margin-top:0;margin-bottom:0}.footnotes>ol>li:target:not([scroll-focus]),.footnotes>ol>li[scroll-focus=true]>p{background-color:var(--footnote-target-bg);width:fit-content;-webkit-transition:background-color 1.5s ease-in-out;transition:background-color 1.5s ease-in-out}a.footnote{margin-left:1px;margin-right:1px;padding-left:2px;padding-right:2px;border-bottom-style:none !important;-webkit-transition:background-color 1.5s ease-in-out;transition:background-color 1.5s ease-in-out}sup:target:not([scroll-focus]),sup[scroll-focus=true]>a.footnote{background-color:var(--footnote-target-bg)}a.reversefootnote{font-size:.6rem;position:absolute;line-height:1;padding-top:.5em;margin-left:.5em;border-bottom-style:none !important}.post h1{margin-top:3rem;margin-bottom:1rem}.post em{padding-right:.2rem}.table-wrapper{overflow-x:auto;margin-bottom:1.5rem}.table-wrapper>table{min-width:100%;overflow-x:auto;border-spacing:0}.table-wrapper>table thead{border-bottom:solid 2px rgba(210,215,217,.75)}.table-wrapper>table tbody tr{border-bottom:1px solid var(--tb-border-color)}.table-wrapper>table tbody tr:nth-child(2n){background-color:var(--tb-even-bg)}.table-wrapper>table tbody tr:nth-child(2n+1){background-color:var(--tb-odd-bg)}.pageviews .fa-spinner{font-size:80%}.post-meta{font-size:.85rem;word-spacing:1px}.post-meta a:not(:last-child){margin-right:2px}.post-content{line-height:1.8;margin-top:2rem;overflow-wrap:break-word;word-wrap:break-word}.post-content img[data-src]{margin:.5rem 0}blockquote .post-content a{color:var(--link-color)}.post-content a.img-hyperlink>img[data-src].left{float:left;margin:.75rem 1rem 1rem 0}.post-content a.img-hyperlink>img[data-src].right{float:right;margin:.75rem 0 1rem 1rem}.post-content a.img-hyperlink+em{display:block;text-align:center;font-style:normal;font-size:80%;padding:0;color:#6d6c6c}.post-content>p>img[data-src]:not(.normal):not(.left):not(.right){position:relative;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%)}.post-content p{font-size:1.08rem}.post-content p>img[data-src]+em{display:block;text-align:center;font-style:normal;font-size:80%;padding:0;color:#6d6c6c}.post-content p>img[data-src].left{float:left;margin:.75rem 1rem 1rem 0}.post-content p>img[data-src].right{float:right;margin:.75rem 0 1rem 1rem}.post-content ul .task-list-item[hide-bullet]{list-style-type:none}.post-content ul .task-list-item[hide-bullet]>i{margin:0 .4rem .2rem -1.4rem;vertical-align:middle;color:var(--checkbox-color)}.post-content ul .task-list-item[hide-bullet]>i.checked{color:var(--checkbox-checked-color)}.post-content ul input[type=checkbox]{margin:0 .5rem .2rem -1.3rem;vertical-align:middle}.post-content>ol,.post-content>ul{padding-left:2rem}.post-content>ol li+li,.post-content>ul li+li{margin-top:.3rem}.post-content>ol li ol,.post-content>ol li ul,.post-content>ul li ol,.post-content>ul li ul{padding-left:2rem;margin-top:.3rem}.post-content>ol li{padding-left:.25em}.post-content dl>dd{margin-left:1rem}.post-tag{display:inline-block;min-width:2rem;text-align:center;background:var(--tag-bg);border-radius:.3rem;padding:0 .4rem;color:inherit;line-height:1.3rem}.post-tag:not(:last-child){margin-right:.2rem}.post-tag:hover{border-bottom:none;text-decoration:none;color:#d2603a}.btn-lang{border:1px solid !important;padding:1px 3px;border-radius:3px;color:var(--link-color)}.btn-lang:focus{box-shadow:none}.semi-bold{font-weight:600 !important}.loaded{display:block !important}.d-flex.loaded{display:flex !important}.unloaded{display:none !important}.visible{visibility:visible !important}.hidden{visibility:hidden !important}.flex-grow-1{-ms-flex-positive:1 !important;flex-grow:1 !important}.btn-box-shadow{box-shadow:0 0 8px 0 var(--btn-box-shadow) !important}.topbar-up{top:-3rem !important}.no-text-decoration{text-decoration:none}.tooltip-inner{font-size:.7rem;max-width:220px;text-align:left}.disabled{color:#cec4c4;pointer-events:auto;cursor:not-allowed}.hide-border-bottom{border-bottom:none !important}.input-focus{box-shadow:none;border-color:var(--input-focus-border-color) !important;background:center !important;transition:background-color .15s ease-in-out,border-color .15s ease-in-out}#sidebar{padding-left:0;padding-right:0;position:fixed;top:0;left:0;height:100%;overflow-y:auto;width:260px;z-index:99;background:var(--sidebar-bg)}#sidebar a{color:rgba(255,255,255,.5);transition:color .35s ease-in-out;user-select:none}#sidebar a:hover{text-decoration:none;color:#fff}#sidebar #avatar:hover>a{border-color:#fff}#sidebar #avatar>a{display:block;width:6rem;height:6rem;border-radius:50%;border:2px solid #b6b6b6;overflow:hidden;transform:translateZ(0);-webkit-transition:border-color .35s ease-in-out;-moz-transition:border-color .35s ease-in-out;transition:border-color .35s ease-in-out}#sidebar #avatar img{width:100%;height:100%;-webkit-transition:transform .5s;-moz-transition:transform .5s;transition:transform .5s}#sidebar #avatar img:hover{-ms-transform:scale(1.2);-moz-transform:scale(1.2);-webkit-transform:scale(1.2);transform:scale(1.2)}#sidebar .site-title a{font-weight:900;font-size:1.5rem;letter-spacing:.5px}#sidebar .site-subtitle{font-size:95%;color:#828282;line-height:1.2rem;word-spacing:1px;margin:.5rem 1.5rem .5rem 1.5rem;min-height:3rem;user-select:none}#sidebar .nav-link{border-radius:0;font-size:.95rem;font-weight:600;letter-spacing:1px;display:table-cell;vertical-align:middle}#sidebar .nav-item{text-align:center;display:table;height:3.2rem}#sidebar .nav-item:hover .nav-link{color:rgba(248,249,250,.8117647059)}#sidebar .nav-item.active .nav-link{color:#fcfcfc}#sidebar ul{height:16rem;margin-bottom:2rem;padding-left:0}#sidebar ul li{width:100%}#sidebar ul li:last-child a{position:relative;left:1.5px;width:100%}#sidebar ul li:last-child::after{display:table;visibility:hidden;content:"";position:relative;right:1px;width:3px;height:1.6rem;border-radius:1px;background-color:var(--nav-cursor-color);pointer-events:none}#sidebar ul>li.active:nth-child(1)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(1):hover~li:last-child::after{top:-12rem;visibility:visible}#sidebar ul>li.active:nth-child(2)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(2):hover~li:last-child::after{top:-8.8rem;visibility:visible}#sidebar ul>li.active:nth-child(3)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(3):hover~li:last-child::after{top:-5.6rem;visibility:visible}#sidebar ul>li.active:nth-child(4)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(4):hover~li:last-child::after{top:-2.4rem;visibility:visible}#sidebar ul>li.active:nth-child(5):last-child::after,#sidebar ul>li.nav-item:nth-child(5):last-child:hover::after{top:.8rem;visibility:visible}#sidebar .sidebar-bottom{font-size:1.2rem;margin-bottom:2.1rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}#sidebar .sidebar-bottom #mode-toggle-wrapper,#sidebar .sidebar-bottom a{width:2.4rem;text-align:center}#sidebar .sidebar-bottom #mode-toggle-wrapper i{color:rgba(255,255,255,.5);transition:color .35s ease-in-out;user-select:none;margin:0;font-size:1.05rem;text-align:center;position:relative;bottom:1px}#sidebar .sidebar-bottom .icon-border{background-color:#525354;content:"";width:3px;height:3px;border-radius:50%;position:relative;top:12px}#sidebar .sidebar-bottom a:hover,#sidebar .sidebar-bottom #mode-toggle-wrapper i:hover{color:#fff}@media(hover: hover){#sidebar ul>li:last-child::after{-webkit-transition:top .5s ease;-moz-transition:top .5s ease;-o-transition:top .5s ease;transition:top .5s ease}}.profile-wrapper{margin-top:2rem;width:100%}#search-result-wrapper{display:none;height:100%;overflow:auto}#search-result-wrapper .post-content{margin-top:2rem}#topbar-wrapper{height:3rem;position:fixed;top:0;left:260px;right:0;transition:top .2s ease-in-out;z-index:50;border-bottom:1px solid rgba(0,0,0,.07);box-shadow:0 3px 5px 0 rgba(0,0,0,.05);background-color:var(--topbar-wrapper-bg)}#topbar i{color:#999}#topbar #breadcrumb{font-size:1rem;color:gray;padding-left:.5rem}#topbar #breadcrumb span:not(:last-child)::after{content:"›";padding:0 .3rem}#sidebar-trigger,#search-trigger{display:none}#search-wrapper{display:flex;width:95%;border-radius:1rem;border:1px solid var(--search-wrapper-bg);background:var(--search-wrapper-bg);padding:0 .5rem}#search-wrapper i{z-index:2;font-size:.9rem;color:var(--search-icon-color)}#search-wrapper .fa-times-circle{visibility:hidden}#search-cancel{color:var(--link-color);margin-left:1rem;display:none}#search-input{background:center;border:0;border-radius:0;padding:.18rem .3rem;color:var(--text-color);font-size:95%}#search-input:focus{box-shadow:none;background:center}#search-input:focus.form-control::-webkit-input-placeholder{opacity:.6}#search-input:focus.form-control::-moz-placeholder{opacity:.6}#search-input:focus.form-control:-ms-input-placeholder{opacity:.6}#search-input:focus.form-control::placeholder{opacity:.6}#search-hints{display:none}#search-hints .post-tag{display:inline-block;line-height:1rem;font-size:1rem;background:var(--search-tag-bg);border:none;padding:.5rem;margin:0 1rem 1rem 0}#search-hints .post-tag::before{content:"#";color:var(--text-muted-color);padding-right:.2rem}#search-results{padding-bottom:6rem}#search-results a{font-size:1.4rem;line-height:2.5rem}#search-results>div{width:100%}#search-results>div:not(:last-child){margin-bottom:1rem}#search-results>div i{color:#818182;margin-right:.15rem;font-size:80%}#search-results>div>p{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}#topbar-title{display:none;font-size:1.1rem;font-weight:600;font-family:sans-serif;color:var(--topbar-text-color);text-align:center;width:70%;overflow:hidden;text-overflow:ellipsis;word-break:keep-all;white-space:nowrap}#mask{display:none;position:fixed;top:0;right:0;bottom:0;left:0;height:100%;width:100%;z-index:1}[sidebar-display] #mask{display:block !important}#main-wrapper{background-color:var(--main-wrapper-bg);position:relative;min-height:100vh;padding-bottom:5rem;padding-left:0;padding-right:0}#main>div.row:first-child>div:nth-child(1),#main>div.row:first-child>div:nth-child(2){margin-top:3rem}#main>div.row:first-child>div:first-child{min-height:calc(100vh - 3rem - 5rem - 35rem)}#post-wrapper{min-height:calc( + 100vh - 3rem - 5rem - 35rem) !important}#topbar-wrapper.row,#main>.row,#search-result-wrapper>.row{margin-left:0;margin-right:0}#back-to-top{display:none;z-index:1;cursor:pointer;position:fixed;background:var(--button-bg);color:var(--btn-backtotop-color);height:2.6em;width:2.7em;border-radius:50%;border:1px solid var(--btn-backtotop-border-color);transition:.2s ease-out;-webkit-transition:.2s ease-out}#back-to-top:hover{transform:translate3d(0, -5px, 0);-webkit-transform:translate3d(0, -5px, 0)}@media all and (max-width: 576px){#main>div.row:first-child>div:first-child{min-height:calc(100vh - 3rem - 6rem)}#post-wrapper{min-height:calc( + 100vh - 3rem - 6rem - 35rem) !important}#post-wrapper h1{margin-top:2.2rem;font-size:1.55rem}#avatar>a{width:5rem;height:5rem}.site-subtitle{margin-left:1.8rem;margin-right:1.8rem}#main-wrapper{padding-bottom:6rem}footer{height:6rem}footer>div.d-flex{width:100%;padding:1.5rem 0;margin-bottom:.3rem;flex-wrap:wrap;-ms-flex-pack:distribute !important;justify-content:space-around !important}footer .footer-left,footer .footer-right{text-align:center}}@media all and (max-width: 830px){#topbar-wrapper,#main-wrapper,#sidebar{-webkit-transition:transform .4s ease;transition:transform .4s ease}html,body{overflow-x:hidden}.footnotes ol>li{padding-top:3.5rem;margin-top:-3.2rem}.footnotes ol>li:first-child{margin-top:-3.5rem}[sidebar-display] #sidebar{transform:translateX(0)}[sidebar-display] #topbar-wrapper,[sidebar-display] #main-wrapper{transform:translateX(260px)}#sidebar{transform:translateX(-260px);-webkit-transform:translateX(-260px)}#sidebar .cursor{-webkit-transition:none;-moz-transition:none;transition:none}#main-wrapper{padding-top:3rem}#search-result-wrapper{width:100%}#breadcrumb,#search-wrapper{display:none}#topbar-wrapper{left:0}.topbar-up{top:0 !important}#main>div.row:first-child>div:nth-child(1),#main>div.row:first-child>div:nth-child(2){margin-top:0}#topbar-title,#sidebar-trigger,#search-trigger{display:block}#search-wrapper.loaded~a{margin-right:1rem}#search-wrapper .fa-times-circle{right:5.2rem}#search-input{margin-left:0;width:95%}#search-result-wrapper .post-content{letter-spacing:0}#search-hints{display:block;padding:0 1rem}#tags{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}#page h1.dynamic-title{display:none}#page h1.dynamic-title~.post-content{margin-top:3rem}}@media all and (min-width: 577px)and (max-width: 1199px){footer>.d-flex>div{width:312px}}@media all and (min-width: 831px){html{overflow-y:scroll}#main-wrapper{margin-left:260px}.profile-wrapper{margin-top:3rem}#search-wrapper{width:22%;min-width:150px}#search-result-wrapper{margin-top:3rem}div.post-content .table-wrapper>table{min-width:70%}#back-to-top{bottom:5.5rem;right:1.2rem}.topbar-up{box-shadow:none !important}#topbar-title{text-align:left}footer>div.d-flex{width:92%}}@media all and (min-width: 992px)and (max-width: 1024px){#main-wrapper .col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 96%;flex:0 0 96%;max-width:96%}}@media all and (min-width: 832px)and (max-width: 1199px){#sidebar{width:210px}#sidebar .sidebar-bottom a,#sidebar .sidebar-bottom span{width:2rem}#sidebar .sidebar-bottom .icon-border{left:-3px}#topbar-wrapper{left:210px}#search-results>div{max-width:700px}.site-title{font-size:1.3rem;margin-left:0 !important}.site-subtitle{margin-left:1rem;margin-right:1rem;font-size:90%}#main-wrapper{margin-left:210px}#breadcrumb{width:65%;overflow:hidden;text-overflow:ellipsis;word-break:keep-all;white-space:nowrap}}@media all and (max-width: 1199px){#panel-wrapper{display:none}#topbar{padding:0}#main>div.row{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}}@media all and (min-width: 1200px){#main>div.row>div.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%;padding-left:3%}#topbar{padding:0;max-width:1070px}#panel-wrapper{max-width:300px}#back-to-top{bottom:6.5rem;right:4.3rem}#search-input{-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}#search-results>div{width:46%}#search-results>div:nth-child(odd){margin-right:1.5rem}#search-results>div:nth-child(even){margin-left:1.5rem}#search-results>div:last-child:nth-child(odd){position:relative;right:24.3%}.post-content p{font-size:1.03rem}footer>div.d-felx{width:85%}}@media all and (min-width: 1400px){#main>div.row{padding-left:calc((100% - 1150px)/2)}#main>div.row>div.col-xl-8{max-width:850px}#search-result-wrapper{padding-right:2rem}#search-result-wrapper>div{max-width:1110px}#search-wrapper .fa-times-circle{right:2.6rem}}@media all and (min-width: 1400px)and (max-width: 1650px){#topbar{padding-right:2rem}}@media all and (min-width: 1650px){#breadcrumb{padding-left:0}#main>div.row>div.col-xl-8{padding-left:0}#main>div.row>div.col-xl-8>div:first-child{padding-left:.55rem !important;padding-right:1.9rem !important}#main-wrapper{margin-left:350px}#panel-wrapper{margin-left:calc((100% - 1150px)/10)}#topbar-wrapper{left:350px}#topbar{max-width:1150px}#search-wrapper{margin-right:3%}#sidebar{width:350px}#sidebar .profile-wrapper{margin-top:4rem;margin-bottom:1rem}#sidebar .profile-wrapper.text-center{text-align:left !important}#sidebar .profile-wrapper .site-subtitle,#sidebar .profile-wrapper .site-title,#sidebar .profile-wrapper #avatar{margin-left:4.5rem}#sidebar .profile-wrapper #avatar>a{width:6.2rem;height:6.2rem}#sidebar .profile-wrapper #avatar>a.mx-auto{margin-left:0 !important}#sidebar .profile-wrapper .site-title a{font-size:1.7rem;letter-spacing:1px}#sidebar .profile-wrapper .site-subtitle{word-spacing:0;margin-top:.3rem}#sidebar ul{padding-left:2.5rem}#sidebar ul>li:last-child>a{position:static}#sidebar ul .nav-item{text-align:left}#sidebar ul .nav-item .nav-link>span{letter-spacing:3px}#sidebar ul .nav-item .nav-link>i{border:1px solid;border-radius:50%;width:1.65rem;height:1.65rem;line-height:1.5rem;font-size:.6rem;padding-top:1px;padding-left:1px;position:relative;bottom:1px}#sidebar ul .nav-item .nav-link>i.unloaded{display:inline-block !important}#sidebar .sidebar-bottom{padding-left:3.5rem;width:100%}#sidebar .sidebar-bottom.justify-content-center{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}#sidebar .sidebar-bottom a{font-size:1rem;width:3rem}#sidebar .sidebar-bottom i{border:1px solid;border-radius:50%;width:2rem;height:2rem;padding-top:.44rem;margin-top:.7rem;bottom:0}#sidebar .sidebar-bottom #mode-toggle-wrapper{width:3rem}#sidebar .sidebar-bottom #mode-toggle-wrapper i{top:11px}#sidebar .sidebar-bottom .icon-border{top:26px}footer>div.d-flex{width:87%;max-width:1140px}#search-result-wrapper>div{max-width:1150px}}@media all and (min-width: 1700px){#topbar-wrapper{padding-right:calc(100% - 350px - (1920px - 350px))}#topbar{max-width:calc(1150px + 20px)}#main>div.row{padding-left:calc((100% - 1150px - 2%)/2)}#panel-wrapper{margin-left:3%}footer{padding-left:0;padding-right:calc(100% - 350px - 1180px)}#back-to-top{right:calc(100% - 1920px + 15rem)}}@media(min-width: 1920px){#main>div.row{padding-left:190px}#search-result-wrapper{padding-right:calc(100% - 350px - 1180px)}#panel-wrapper{margin-left:41px}}.pagination{font-size:1rem}.pagination a:hover{text-decoration:none}.pagination .page-item .page-link{color:var(--btn-patinator-text-color);width:2.5rem;height:2.5rem;padding:0;text-align:center;display:-webkit-box;display:flex;-webkit-box-pack:center;justify-content:center;-webkit-box-align:center;align-items:center;border-radius:50%;border:1px solid var(--btn-paginator-border-color);font-family:"Lato",sans-serif;background-color:var(--button-bg)}.pagination .page-item .page-link:hover{background-color:var(--btn-paginator-hover-color)}.pagination .page-item.active .page-link{background-color:var(--btn-active-bg);border-color:var(--btn-active-border-color);box-shadow:0 0 8px 0 var(--btn-paginator-shadow) !important;color:var(--btn-text-color)}.pagination .page-item.disabled{cursor:not-allowed}.pagination .page-item.disabled .page-link{color:rgba(108,117,125,.57);border-color:var(--btn-paginator-border-color);background-color:var(--button-bg)}.pagination .page-item:first-child .page-link,.pagination .page-item:last-child .page-link{border-radius:50%}.pagination .page-item:not(:last-child){margin-right:.7rem}#post-list{margin-top:1rem;padding-right:.5rem}#post-list .post-preview{padding-top:1.5rem;padding-bottom:1rem;border-bottom:1px solid var(--main-border-color)}#post-list .post-preview h1{font-size:1.4rem;margin:0}#post-list .post-preview .post-meta i{font-size:.73rem}#post-list .post-preview .post-meta span:not(:last-child){margin-right:1.2rem}#post-list .post-preview .post-content{margin-top:.6rem;margin-bottom:.6rem;color:var(--post-list-text-color)}#post-list .post-preview .post-content>p{margin:0;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}#post-list .post-preview .pin>i{transform:rotate(45deg);padding-left:3px;color:var(--pin-color)}#post-list .post-preview .pin>span{display:none}@media all and (max-width: 830px){.pagination{justify-content:center}}@media all and (min-width: 831px){#post-list{margin-top:1.5rem}#post-list .post-preview .post-meta .pin{background:var(--pin-bg);border-radius:5px;line-height:1.4rem;height:1.3rem;margin-top:3px;padding-left:1px;padding-right:6px}#post-list .post-preview .post-meta .pin>span{display:inline}.pagination{font-size:.85rem}.pagination .page-item .page-link{width:2.2rem;height:2.2rem}}@media all and (max-width: 1200px){#post-list{padding-right:0}}.timeago::before{content:attr(prefix)}#post-wrapper .post-meta>div:nth-child(2)>span:not(:first-child)::before{content:"•";color:rgba(158,158,158,.8);padding-left:.2rem;padding-right:.4rem}#post-wrapper .post-meta #pv::after{content:" views"}#post-wrapper .post-meta .readtime::after{content:" read"}.post-content .preview-img{position:relative;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);margin-top:0;margin-bottom:2.5rem !important}.post-tail-wrapper{margin-top:6rem;border-bottom:1px double var(--main-border-color);font-size:.85rem}.post-tags{line-height:2rem}.post-navigation{padding-top:3rem;padding-bottom:4rem}.post-navigation .btn{width:50%;position:relative;border-color:var(--btn-border-color);color:var(--link-color)}.post-navigation .btn:hover{background:#2a408e;color:#fff;border-color:#2a408e}.post-navigation .btn.disabled{width:50%;position:relative;border-color:var(--btn-border-color);pointer-events:auto;cursor:not-allowed;background:none;color:gray}.post-navigation .btn.disabled:hover{border-color:none}.post-navigation .btn.btn-outline-primary.disabled:focus{box-shadow:none}.post-navigation .btn::before{color:var(--text-muted-color);font-size:.65rem;text-transform:uppercase;content:attr(prompt)}.post-navigation .btn:first-child{border-top-right-radius:0;border-bottom-right-radius:0;left:.5px}.post-navigation .btn:last-child{border-top-left-radius:0;border-bottom-left-radius:0;right:.5px}.post-navigation p{font-size:1.1rem;line-height:1.5rem;margin-top:.3rem;white-space:normal}@keyframes fade-up{from{opacity:0;position:relative;top:2rem}to{opacity:1;position:relative;top:0}}#toc-wrapper{border-left:1px solid rgba(158,158,158,.17);position:-webkit-sticky;position:sticky;top:4rem;transition:top .2s ease-in-out;animation:fade-up .8s}#toc-wrapper.topbar-down{top:6rem}#toc-wrapper>span{color:var(--label-color);font-size:inherit;font-weight:600;display:block;line-height:1.2;padding-top:.5rem;padding-bottom:.5rem;margin-top:0;margin-bottom:0;letter-spacing:-0.02em}#toc li>a{line-height:1rem;padding-top:.5rem;padding-bottom:.5rem}#related-posts>h3{color:var(--label-color);font-size:1.1rem;font-weight:600}#related-posts .card{border-color:var(--card-border-color);background-color:var(--card-bg);box-shadow:0 0 5px 0 var(--card-box-shadow);-webkit-transition:all .3s ease-in-out;-moz-transition:all .3s ease-in-out;transition:all .3s ease-in-out}#related-posts .card h3{color:var(--text-color)}#related-posts .card:hover{-webkit-transform:translate3d(0, -3px, 0);transform:translate3d(0, -3px, 0);box-shadow:0 10px 15px -4px rgba(0,0,0,.15)}#related-posts .timeago{color:var(--relate-post-date)}#related-posts p{font-size:.9rem;margin-bottom:.5rem;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}#related-posts a:hover{text-decoration:none}#related-posts ul{list-style-type:none;padding-inline-start:1.5rem}#related-posts ul>li::before{background:#c2c9d4;width:5px;height:5px;border-radius:1px;display:block;content:"";position:relative;top:1rem;right:1rem}#post-extend-wrapper{min-height:2rem}#post-extend-wrapper #disqus_thread{margin-bottom:2rem}.post-tail-bottom a{color:inherit}.share-wrapper .share-icons>i:hover,.share-wrapper .share-icons a:hover>i{color:var(--btn-share-hover-color) !important}.share-wrapper{vertical-align:middle;user-select:none}.share-wrapper .share-icons{font-size:1.2rem}.share-wrapper .share-icons a:not(:last-child){margin-right:.25rem}.share-wrapper .share-icons a:hover{text-decoration:none}.share-wrapper .share-icons>i{padding-top:.35rem}.share-wrapper .share-icons .fab.fa-twitter{color:var(--btn-share-color, rgb(29, 161, 242))}.share-wrapper .share-icons .fab.fa-facebook-square{color:var(--btn-share-color, rgb(66, 95, 156))}.share-wrapper .share-icons .fab.fa-telegram{color:var(--btn-share-color, rgb(39, 159, 217))}.share-wrapper .share-icons .fab.fa-weibo{color:var(--btn-share-color, rgb(229, 20, 43))}.share-wrapper .fas.fa-link{color:var(--btn-share-color, rgb(171, 171, 171))}.share-label{color:inherit;font-size:inherit;font-weight:400}.share-label::after{content:":"}.license-wrapper{line-height:1.2rem}.license-wrapper>a{font-weight:600}.license-wrapper span:last-child{font-size:.85rem}@media all and (max-width: 576px){.post-tail-bottom{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.post-tail-bottom>div:first-child{width:100%;margin-top:1rem}.post-content>div[class^=language-]{margin-left:-1.25rem;margin-right:-1.25rem;border-radius:0}.post-content>div[class^=language-]::before{right:1rem}}@media all and (max-width: 768px){.post-content>p>img{max-width:calc(100% + 1rem)}}@media all and (min-width: 768px){#post-wrapper .post-meta>div:not(:first-child)::before{content:"•";color:rgba(158,158,158,.8);padding-left:.5rem;padding-right:.2rem}#post-wrapper .post-meta.flex-column{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}}@media all and (max-width: 830px){.post-navigation{padding-left:0;padding-right:0;margin-left:-0.5rem;margin-right:-0.5rem}}.tag{border-radius:.7em;padding:6px 8px 7px;margin-right:.8rem;line-height:3rem;letter-spacing:0;border:1px solid var(--tag-border) !important;box-shadow:0 0 3px 0 var(--tag-shadow)}.tag span{margin-left:.6em;font-size:.7em;font-family:"Oswald",sans-serif}#archives ul li:first-child::before,#archives ul li::after{content:"";width:4px;left:75px;display:inline-block;float:left;position:relative;background-color:var(--timeline-color)}#archives{letter-spacing:.03rem}#archives span.lead{font-size:1.5rem;position:relative;left:8px}#archives span.lead::after{content:"";display:block;position:relative;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;width:12px;height:12px;top:-26px;left:63px;border:3px solid;background-color:var(--timeline-year-dot-color);border-color:var(--timeline-node-bg);box-shadow:0 0 2px 0 #c2c6cc;z-index:1}#archives span.lead:not(:first-child){position:relative;left:4px}#archives span.lead:not(:first-child)::after{left:67px}#archives ul li{font-size:1.1rem;line-height:3rem}#archives ul li div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#archives ul li div a{margin-left:2.5rem;position:relative;top:.1rem}#archives ul li:nth-child(odd){background-color:var(--main-wrapper-bg, #fff);background-image:linear-gradient(to left, #fff, #fbfbfb, #fbfbfb, #fbfbfb, #fff)}#archives ul li::after{height:2.8rem;top:-1.3rem}#archives ul li:first-child::before{height:3.06rem;top:-1.61rem}#archives ul:not(:last-child)>li:last-child::after{height:3.4rem}#archives ul:last-child>li:last-child::after{display:none}#archives .date{white-space:nowrap;display:inline-block}#archives .date.month{width:1.4rem;text-align:center}#archives .date.month~a::before{content:"";display:inline-block;position:relative;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;width:8px;height:8px;float:left;top:1.35rem;left:69px;background-color:var(--timeline-node-bg);box-shadow:0 0 3px 0 #c2c6cc;z-index:1}#archives .date.day{font-size:85%;font-family:"Lato",sans-serif;text-align:center;margin-right:-2px;width:1.2rem;position:relative;left:-0.15rem}@media all and (max-width: 576px){#archives{margin-top:-1rem}#archives ul{letter-spacing:0}}.categories .card-header>span>i:first-child,.categories .list-group-item>i{color:gray}.categories{margin-bottom:2rem}.categories .card-header{padding-right:12px}.categories i.far,.categories i.fas{font-size:86%}.categories .list-group-item{border-left:none;border-right:none;padding-left:2rem}.categories .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.category-trigger{width:1.7rem;height:1.7rem;border-radius:50%;text-align:center;color:#6c757d !important}.category-trigger>i.fas{position:relative;height:.7rem;width:1rem;transition:300ms ease all}@media(hover: hover){.category-trigger:hover{background-color:var(--categories-hover-bg)}}.rotate{-ms-transform:rotate(-90deg);-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.dash{margin:0 .5rem .6rem .5rem;border-bottom:2px dotted var(--dash-color)}#page-category ul>li,#page-tag ul>li{line-height:1.5rem;padding:.6rem 0}#page-category ul>li::before,#page-tag ul>li::before{background:#999;width:5px;height:5px;border-radius:50%;display:block;content:"";position:relative;top:.6rem;margin-right:.5rem}#page-category ul>li>a,#page-tag ul>li>a{font-size:1.1rem}#page-category ul>li>span:last-child,#page-tag ul>li>span:last-child{white-space:nowrap}#page-tag h1>i{font-size:1.2rem}#page-category h1>i{font-size:1.25rem}#page-category a:hover,#page-tag a:hover,#access-lastmod a:hover{margin-bottom:-1px}@media all and (max-width: 576px){#page-category ul>li::before,#page-tag ul>li::before{margin:0 .5rem}#page-category ul>li>a,#page-tag ul>li>a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}.code-tab{overflow:hidden;border:1px solid #ccc;background-color:#f1f1f1}.code-tab button{background-color:inherit;float:left;border:none;outline:none;cursor:pointer;padding:14px 16px;transition:.3s}.code-tab button:hover{background-color:#ddd}.code-tab button.active{background-color:#ccc}html[mode=dark] .code-tab{background-color:#303030}html[mode=dark] .code-tab button{color:#fff}html[mode=dark] .code-tab button:hover{background-color:#242424}html[mode=dark] .code-tab button.active{background-color:#1c1c1c}.code-tabcontent{display:none;padding:6px 12px;border:1px solid #ccc;border-top:none}code{font-family:"Fira Code",monospace}code pre{font-family:"Fira Code",monospace}@supports(font-variation-settings: normal){code{font-family:"Fira Code VF",monospace}code pre{font-family:"Fira Code VF",monospace}}.unlock .show{display:none}.unlock .button_unlock{display:block;font-size:larger;border-radius:15px;border-width:0;padding:3px 10px}.unlock .hint{background:rgba(255,193,7,.6901960784)}.unlock .solution{background:rgba(26,178,26,.6901960784)}.button_unlock{display:none}.code_fold button{padding:0;background:0;border:0;color:#28a745;font-size:1em}.code_fold button::before{font-family:"Font Awesome 5 Free";font-weight:900}.code_fold button:focus{outline:0px}.code_fold_closed button::before{content:""}.code_fold_opened button::before{content:""}.code_fold_closed .code_fold_span{display:none}.highlight_backline_green{background-color:rgba(25,250,25,.1725490196)}.highlight_backline_yellow{background-color:rgba(227,250,25,.1725490196)}.highlight_backline_blue{background-color:rgba(50,94,240,.1725490196)}td.rouge-gutter{vertical-align:top;padding-top:1.5rem}td.rouge-code{vertical-align:top}.neutral{background-color:rgba(102,102,102,.3568627451);padding:2px}.red{background-color:rgba(255,48,48,.3568627451);padding:2px}.blue{background-color:rgba(72,66,255,.3568627451);padding:2px}@media(min-width: 1400px){#main>div.row>div.col-xl-8{max-width:max(60%,850px)}}@media(min-width: 1700px){#panel-wrapper{margin-left:calc((100% - 1150px)/10)}}/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/assets/css/style.css.map b/assets/css/style.css.map new file mode 100644 index 0000000..b05a388 --- /dev/null +++ b/assets/css/style.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/jekyll-theme-chirpy.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/addon/commons.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/addon/module.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/addon/syntax.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/colors/light-syntax.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/colors/dark-syntax.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/colors/light-typography.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/colors/dark-typography.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/addon/variables.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/layout/home.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/layout/post.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/layout/tags.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/layout/archives.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/layout/categories.scss","../../../../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-3.3.2/_sass/layout/category-tag.scss","style.scss"],"names":[],"mappings":"CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GCGQ,4GCGR,iCACE,2BACA,gBACA,gDAGF,2BACE,4BACA,uCAGF,+DACE,mBACA,cACA,mBAGF,8QACE,yBACA,gCACA,qBAGF,qEACE,wBAGF,iDACE,oDAGF,qKACE,mBAIA,oEACE,gBACA,mBAIJ,YACE,mBACA,mBC3CF,kCC8DE,8BACA,mCACA,kCACA,yCACA,0BAnEA,wFACA,2FACA,yGACA,0FACA,0FACA,6FACA,8GACA,6FACA,8GACA,oGACA,6FACA,2EACA,2EACA,iGACA,2EACA,2EACA,iFACA,2EACA,2EACA,4FACA,4FACA,4FACA,4FACA,4FACA,4FACA,yEACA,4EACA,2EACA,8EACA,4FACA,2EACA,+FACA,6EACA,4FACA,4FACA,4FACA,2EACA,2EACA,2EACA,4FACA,yEACA,2EACA,2EACA,2EACA,2EACA,8EACA,8EACA,8EACA,8EACA,8EACA,8EACA,8EACA,8EACA,8EACA,8EACA,8EACA,2EACA,2EACA,2EACA,2EACA,2EDtDF,gBENE,8BACA,mCACA,kCACA,yCACA,0BAGE,6CAGF,kCACA,0CAGA,0EACA,2EACA,4CACA,uEACA,4CACA,4CACA,4CACA,4CACA,4CACA,6CACA,6CACA,6CACA,6CACA,+DACA,gDACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,4CACA,4CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,4CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,6CACA,sEACA,sEFlEF,mCACE,iCEXA,8BACA,mCACA,kCACA,yCACA,0BAGE,6EAGF,uDACA,+DAGA,0GACA,4GACA,2EACA,wGACA,2EACA,2EACA,2EACA,2EACA,2EACA,6EACA,6EACA,6EACA,6EACA,+FACA,gFACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,2EACA,2EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,2EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,6EACA,sGACA,sGF5DA,iBC+CA,8BACA,mCACA,kCACA,yCACA,0BAnEA,uDACA,4DACA,wEACA,2DACA,2DACA,6DACA,8EACA,6DACA,8EACA,oEACA,6DACA,2CACA,2CACA,iEACA,2CACA,2CACA,iDACA,2CACA,2CACA,4DACA,4DACA,4DACA,4DACA,4DACA,4DACA,0CACA,6CACA,2CACA,8CACA,4DACA,2CACA,+DACA,6CACA,4DACA,4DACA,4DACA,2CACA,2CACA,2CACA,4DACA,0CACA,2CACA,2CACA,2CACA,2CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,2CACA,2CACA,2CACA,2CACA,4CDrCF,uDACE,qCAGF,sCACE,kBAGF,sNACE,eAWF,mBAIE,qCACA,iBACA,oBAGF,WAQE,cACA,mBACE,kBACA,oBACA,iBACA,iBACA,oCACA,4DACA,yBACA,wBACA,sBACA,qBACA,oBACA,iBAEF,eACE,gBACA,UA1Ca,OA2Cb,mBACA,iBAEF,iBACE,UACA,SACA,wBACE,iBACA,kBAGJ,cACE,UACA,SAIJ,KACE,qBACA,iBACA,kBACA,aAEA,uBACE,UAnEa,OAoEb,gBACA,kBACA,uCAGO,yBACP,iBACA,cAGO,+BACP,mBAGF,kCACE,cAIJ,cACE,kCAIA,gBACE,yBACA,8BACA,oBAWA,kMACE,aAQN,8BACE,mBACA,kBACA,WACA,eACA,gBACA,gBACA,oCACA,yBAGF,yBACE,8BACE,cAIJ,0BACE,8BACE,cFxJJ,kCKjBE,mBACA,mBACA,yBACA,6BACA,4BACA,sBACA,uBACA,gCACA,iCACA,sBACA,gCACA,yBACA,qBACA,2BACA,kBACA,+BACA,sCACA,0BACA,0BACA,kCAGA,0FAIA,4BAGA,2BACA,qCACA,6BACA,yBACA,6BACA,oDAGA,gCACA,oCACA,qCACA,yBACA,mCACA,0BACA,sCACA,gCACA,kBACA,qBAGA,2CACA,6BACA,2DACA,uBACA,0CACA,+BACA,sBACA,sCACA,gCACA,+CACA,qBAGA,uCACA,4BACA,mCAGA,2BACA,gCACA,uBL3DE,4DACE,eAWN,gBMtBE,mCACA,kCACA,qCACA,qCACA,wCACA,+CACA,2BACA,0CAGA,iCACA,yBACA,uCACA,iCACA,0CACA,qCACA,6BACA,2CACA,4CACA,oCACA,yCACA,sDACA,yCACA,kCACA,kCACA,mCACA,4CAGA,uCACA,gEAGA,uCAGA,2CACA,8CACA,6CACA,qCACA,4CACA,oCACA,sDACA,+CACA,wBACA,qBAGA,oCACA,0BACA,6BACA,oCACA,8BACA,oCACA,uCACA,2BACA,iCACA,4CACA,2BACA,qCACA,0CAGA,8BACA,8BACA,+BACA,8BAGA,qCACA,uCAGA,uCACA,kCACA,iDAGA,kCAEA,kCACE,uBAGF,mBACE,sCAIF,wRAKE,sCACA,kDAIF,kEAEE,gCAIA,yCACE,uCAEF,6CACE,iBACA,kBACA,kBACA,sCACA,wDACE,mCAKN,4CACE,+HNrHA,6BACE,0BAsBN,mCACE,iCM3BA,mCACA,kCACA,qCACA,qCACA,wCACA,+CACA,2BACA,0CAGA,iCACA,yBACA,uCACA,iCACA,0CACA,qCACA,6BACA,2CACA,4CACA,oCACA,yCACA,sDACA,yCACA,kCACA,kCACA,mCACA,4CAGA,uCACA,gEAGA,uCAGA,2CACA,8CACA,6CACA,qCACA,4CACA,oCACA,sDACA,+CACA,wBACA,qBAGA,oCACA,0BACA,6BACA,oCACA,8BACA,oCACA,uCACA,2BACA,iCACA,4CACA,2BACA,qCACA,0CAGA,8BACA,8BACA,+BACA,8BAGA,qCACA,uCAGA,uCACA,kCACA,iDAGA,kCAEA,qEACE,uBAGF,uCACE,sCAIF,qjBAKE,sCACA,kDAIF,sIAEE,gCAIA,mFACE,uCAEF,2FACE,iBACA,kBACA,kBACA,sCACA,iHACE,mCAKN,yFACE,+HNrHA,2DACE,0BA4BJ,iBKhCA,mBACA,mBACA,yBACA,6BACA,4BACA,sBACA,uBACA,gCACA,iCACA,sBACA,gCACA,yBACA,qBACA,2BACA,kBACA,+BACA,sCACA,0BACA,0BACA,kCAGA,0FAIA,4BAGA,2BACA,qCACA,6BACA,yBACA,6BACA,oDAGA,gCACA,oCACA,qCACA,yBACA,mCACA,0BACA,sCACA,gCACA,kBACA,qBAGA,2CACA,6BACA,2DACA,uBACA,0CACA,+BACA,sBACA,sCACA,gCACA,+CACA,qBAGA,uCACA,4BACA,mCAGA,2BACA,gCACA,uBL3DE,8BACE,gBA0BN,MACE,eAGF,KACE,oBACA,0BACA,wBACA,mCACA,2DAKF,GAGE,iBAGF,GAKE,iBAGF,GAKE,kBAGF,GAKE,kBAGF,GAKE,iBAKA,wBAEE,mBAQJ,IACE,eAGF,WACE,qDACA,kBACA,mCAGF,IACE,eAGF,OACE,kBACA,SACA,eACA,OO9Gc,KP+Gd,gBACA,cACA,wCAEA,kBACE,mBACA,UACA,iBACA,8CACA,mBAEA,sBACE,YAIJ,SACE,yBACA,cCxFF,qBD2FE,eC3FF,qBDiGA,qBACE,iBAMJ,QACE,SACA,+BACA,oBACA,gBACA,mBAEA,mBACE,wBACA,gBAEF,oBACE,SAEF,YACE,kBACA,+CACA,6BACE,mBAGJ,aC1FA,MADwD,mBAExD,UAKe,QAJf,YAH2C,IAS3C,cACA,gBACA,kBACA,qBACA,aACA,gBACA,uBD+EA,sBACE,gBAKF,kCACE,cAGF,uBACE,qBACA,iBACA,iBACA,gBACA,yCACA,oBACA,oBACA,wBACA,6BACE,yBACA,qBACA,WACA,gBAOJ,mBACE,cACA,gBACA,uBACA,oBACA,qBACA,4BACA,gBAGF,kBAOE,cAKJ,cACE,kBACA,iBAEE,kCACE,oBAEF,mBACE,kBACA,aACA,gBAGF,kFAEE,2CACA,kBACA,qDACA,6CAMK,WC3LT,YD4LiB,IC3LjB,aD2LiB,ICvLjB,aDwLiB,ICvLjB,cDuLiB,IAEf,oCACA,qDACA,6CAIO,iEAEP,2CAKO,kBACP,gBACA,kBACA,cACA,iBACA,iBACA,oCAKF,SACE,gBACA,mBAEF,SACE,oBAWJ,eACE,gBACA,qBAEA,qBACE,eACA,gBACA,iBAEA,2BACE,8CAOA,8BACE,+CACA,4CACE,mCAEF,8CACE,kCAYV,uBACE,cAGF,WACE,iBACA,iBAIE,8BACE,iBAQN,cACE,gBACA,gBACA,yBACA,qBAEA,4BACE,eAmCA,2BACE,wBAfA,iDACE,WACA,0BAGF,kDACE,YACA,0BAxBJ,iCACE,cACA,kBACA,kBACA,cACA,UACA,cA4CF,kECtTF,kBACA,SACA,mCACA,+BACA,2BDuTA,gBAGE,kBA1DA,iCACE,cACA,kBACA,kBACA,cACA,UACA,cAWA,mCACE,WACA,0BAGF,oCACE,YACA,0BAuCJ,8CACE,qBAEA,gDACE,6BACA,sBACA,4BACA,wDACE,oCAMN,sCACE,6BACA,sBAKJ,kCAEE,kBAGE,8CACE,iBAGF,4FAEE,kBACA,iBAOJ,oBACE,mBAIJ,oBACE,iBASJ,UACE,qBACA,eACA,kBACA,yBACA,oBACA,gBACA,cACA,mBAEA,2BACE,mBAGF,gBAGE,mBACA,qBACA,cAKJ,UACE,4BACA,gBACA,kBACA,wBACA,gBACE,gBAMJ,WACE,2BAGF,QACE,yBAES,eACP,wBAIJ,UACE,wBAGF,SACE,8BAGF,QACE,6BAGF,aACE,+BACA,uBAGF,gBACE,sDAGF,WACE,qBAGF,oBCxfE,qBD4fF,eACE,gBACA,gBACA,gBAGF,UACE,cACA,oBACA,mBAGF,oBACE,8BAGF,aACE,gBACA,wDACA,6BACA,2EASF,SCngBE,aDogBe,ECngBf,cDmgBe,EAEf,eACA,MACA,OACA,YACA,gBACA,MO/kBqB,MPglBrB,WACA,6BAEA,WCjiBA,MAD2B,qBAE3B,kCACA,iBDkiBE,iBCxiBF,qBD2iBI,WAKF,yBACE,kBAGF,mBACE,cACA,WACA,YACA,kBACA,yBACA,gBACA,wBACA,iDACA,8CACA,yCAGF,qBACE,WACA,YACA,iCACA,8BACA,yBAEA,2BACE,yBACA,0BACA,6BACA,qBAMJ,uBACE,gBACA,iBACA,oBAIJ,wBACE,cACA,cACA,mBACA,iBACA,iCACA,gBACA,iBAGF,mBACE,gBACA,iBACA,gBACA,mBACA,mBACA,sBAGF,mBACE,kBACA,cACA,OOjqBS,OPmqBP,mCACE,oCAIF,oCACE,cAKN,YACE,aACA,mBACA,eAEA,eACE,WAKE,4BACE,kBACA,WACA,WAGF,iCACE,cACA,kBACA,WACA,kBACA,UACA,MAda,IAeb,OOrsBU,OPssBV,kBACA,yCACA,oBAiBA,wHAXJ,IAMM,OALN,mBAUI,wHAXJ,IAMM,QALN,mBAUI,wHAXJ,IAMM,QALN,mBAUI,wHAXJ,IAMM,QALN,mBAeE,kHAhBF,IAMM,MALN,mBAyBJ,yBACE,iBACA,qBCxqBF,YD0qBiB,KCzqBjB,aDyqBiB,KCrqBjB,aDsqBiB,KCrqBjB,cDqqBiB,KAEf,yEACE,aACA,kBAUA,gDCtsBJ,MAD2B,qBAE3B,kCACA,iBDusBM,SACA,kBACA,kBACA,kBACA,WAKJ,sCACE,yBACA,WACA,UACA,WACA,kBACA,kBACA,SAGF,uFAEE,WAON,qBACE,iCACE,gCACA,6BACA,2BACA,yBAIJ,iBACE,gBACA,WAGF,uBACE,aACA,YACA,cACA,qCACE,gBAMJ,gBACE,OO9yBc,KP+yBd,eACA,MACA,WACA,QACA,+BACA,WACA,wCACA,uCACA,0CAIA,UACE,WAGF,oBACE,eACA,WACA,mBAQI,iDACE,YACA,gBAOV,iCAEE,aAGF,gBACE,aACA,UACA,mBACA,0CACA,oCACA,gBACA,kBACE,UACA,gBACA,+BAEF,iCACE,kBAIJ,eACE,wBACA,iBACA,aAGF,cACE,kBACA,SACA,gBACA,qBACA,wBACA,cAEA,oBACE,gBACA,kBAEE,4DCrzBJ,WDszBI,mDCtzBJ,WDuzBI,uDCvzBJ,WDwzBI,8CCxzBJ,WD6zBF,cACE,aAEA,wBACE,qBACA,iBACA,eACA,gCACA,YACA,cACA,qBACA,gCACE,YACA,8BACA,oBAKN,gBACE,oBACA,kBASE,iBACA,mBAGF,oBACE,WAEA,qCACE,mBAGF,sBACE,cACA,oBACA,cAGF,sBACE,gBACA,uBACA,oBACA,qBACA,4BAKN,cACE,aACA,iBACA,gBACA,uBACA,+BACA,kBACA,UACA,gBACA,uBACA,oBACA,mBAaF,MACE,aACA,eACA,MACA,QACA,SACA,OACA,YACA,WACA,UAES,wBACP,yBAMJ,cACE,wCACA,kBACA,iBACA,eOx+Bc,KN+Dd,aD26Be,EC16Bf,cD06Be,EAIf,sFAEE,WOl/BY,KPo/Bd,0CAEE,6CAIJ,cACE;AAAA,4CAIF,2DCn8BE,YDs8Be,ECr8Bf,aDq8Be,EAKjB,aACE,aACA,UACA,eACA,eACA,4BACA,iCACA,aACA,YACA,kBACA,mDACA,wBACA,gCAGF,mBACE,kCACA,0CAKF,kCAIE,0CACE,qCAGF,cACE;AAAA,8CAEA,iBACE,kBACA,kBAIJ,UACE,WACA,YAGF,eCv/BA,YDw/BiB,OCv/BjB,aDu/BiB,OAGjB,cACE,eAzBc,KA4BhB,OACE,OA7Bc,KA8Bd,kBACE,WACA,iBACA,oBACA,eACA,oCACA,wCAEF,yCAEE,mBAON,kCACE,uCACE,sCACA,8BAGF,UAEE,kBAGF,iBACE,mBACA,mBACA,6BACE,mBAMF,2BACE,wBAGF,kEAEE,4BAKJ,SAGE,6BACA,qCAEA,iBACE,wBACA,qBACA,gBAIJ,cAGE,YO9nCY,KPioCd,uBACE,WAGF,4BAEE,aAGF,gBAGE,OAGF,WACE,iBAGF,sFAEE,aAGF,+CAGE,cAIA,yBACE,kBAEF,iCACE,aAIJ,cACE,cACA,UAGF,qCACE,iBAGF,cACE,cACA,eAGF,MACE,mCACA,gCACA,kCAGF,uBACE,aACA,qCACE,iBAMN,yDACE,mBACE,aAKJ,kCAEE,KACE,kBAGF,cACE,YOttCmB,MPytCrB,iBACE,gBAGF,gBACE,UACA,gBAGF,uBACE,gBAGF,sCACE,cAIF,aACE,cACA,aAGF,WACE,2BAGF,cACE,gBAGF,kBACE,WAMJ,yDACE,yBACE,mBACA,iBACA,aACA,eAKJ,yDAEE,SACE,MO7wCkB,MP+wChB,yDAEE,WAEF,sCACE,UAKN,gBACE,WAGF,oBACE,gBAGF,YACE,iBACA,yBAGF,eCtuCA,YDuuCiB,KCtuCjB,aDsuCiB,KAEf,cAGF,cACE,kBAGF,YACE,UACA,gBACA,uBACA,oBACA,oBAMJ,mCACE,eACE,aAGF,QACE,UAGF,cACE,mCACA,gCACA,mCAMJ,mCAEE,2BACE,mBACA,iBACA,aACA,cACA,gBAGF,QACE,UACA,iBAGF,eACE,UOn1Cc,MPs1ChB,aACE,cACA,aAGF,cACE,uCACA,+BAGF,oBACE,UACA,mCACE,oBAEF,oCACE,mBAEF,8CACE,kBACA,YAIJ,gBACE,kBAGF,kBACE,WAKJ,mCAEE,cACE,qCACA,2BACE,gBAIJ,uBACE,mBACA,2BACE,iBAIJ,iCACE,cAKJ,0DACE,QACE,oBAIJ,mCAEE,YACE,eAGF,2BACE,eACA,2CACE,+BACA,gCAIJ,cACE,YO36CkB,MP86CpB,eACE,qCAGF,gBACE,KOn7CkB,MPs7CpB,QACE,iBAGF,gBACE,gBAGF,SACE,MO/7CkB,MPi8ClB,0BACE,gBACA,mBAEA,sCACE,2BAGF,iHACE,mBAMA,oCACE,aACA,cACA,4CACE,yBAQJ,wCACE,iBACA,mBAIJ,yCAGE,eACA,iBAKJ,YACE,oBAGE,4BACE,gBAIJ,sBACE,gBAGE,qCACE,mBAGF,kCCr8CR,iBACA,kBACA,MDo8C8B,QCn8C9B,ODm8C8B,QAEpB,mBACA,gBACA,gBACA,iBACA,kBACA,WAEA,2CACE,gCAQV,yBACE,oBACA,WAEA,gDACE,kCACA,+BACA,sCAGF,2BACE,eACA,WAGF,2BCv+CJ,iBACA,kBACA,MDs+C0B,KCr+C1B,ODq+C0B,KAEpB,mBACA,iBACA,SAGF,8CACE,WAEA,gDACE,SAIJ,sCACE,SAON,kBACE,UACA,iBAIA,2BACE,kBAMN,mCACE,gBAEE,oDAGF,QACE,8BAGF,cACE,0CAGF,eACE,eAGF,OACE,eACA,0CAGF,aACE,mCAKJ,0BACE,cACE,mBAGF,uBACE,0CAGF,eACE,kBQjnDJ,YACE,eACA,oBACE,qBAIA,kCACE,sCACA,aACA,cACA,UACA,kBACA,oBACA,aACA,wBACA,uBACA,yBACA,mBACA,kBACA,mDACA,8BACA,kCACA,wCACE,kDAIF,yCACE,sCACA,4CACA,4DACA,4BAGJ,gCACE,mBACA,2CACE,4BACA,+CACA,kCAGJ,2FAEE,kBAEF,wCACE,mBAMN,WACE,gBACA,oBAEA,yBACE,mBACA,oBACA,iDAMA,4BACE,iBACA,SAIA,sCACE,iBAEF,0DACE,oBAIJ,uCACE,iBACA,oBACA,kCACA,yCAEE,SACA,gBACA,uBACA,oBACA,qBACA,4BAKF,gCACE,wBACA,iBACA,uBAEF,mCACE,aASR,kCACE,YACE,wBAKJ,kCAEE,WACE,kBAEE,yCACE,yBACA,kBACA,mBACA,cACA,eACA,iBACA,kBAEA,8CACE,eAMR,YACE,iBACA,kCACE,aACA,eAON,mCACE,WACE,iBCpIJ,iBACE,qBAKE,yEAZF,YACA,2BACA,aAHc,MAId,cAJ2B,MAkB3B,oCACE,iBAGF,0CACE,gBAKF,2BR+DA,kBACA,SACA,mCACA,+BACA,2BQhEE,aACA,gCAIJ,mBACE,gBACA,kDACA,iBAGF,WACE,iBAGF,iBACE,iBACA,oBAEA,sBAvDA,UACA,kBACA,qCAwDE,wBAEA,4BACE,mBACA,WACA,qBAGF,+BAlEF,UACA,kBACA,qCAmEI,oBACA,mBACA,gBACA,WAEA,qCACE,kBAIJ,yDACE,gBAGF,8BACE,8BACA,iBACA,yBACA,qBAGF,kCACE,0BACA,6BACA,UAGF,iCACE,yBACA,4BACA,WAIJ,mBACE,iBACA,mBACA,iBACA,mBAKJ,mBACE,KACE,UACA,kBACA,SAEF,GACE,UACA,kBACA,OAIJ,aACE,4CACA,wBACA,gBACA,SACA,+BACA,sBACA,yBACE,SAEF,kBRzDA,MADwD,mBAExD,UAKe,QAJf,YAH2C,IAS3C,cACA,gBACA,kBACA,qBACA,aACA,gBACA,uBQgDF,UACE,iBACA,kBACA,qBAMA,kBRvEA,MADwD,mBAExD,UQuEiB,ORtEjB,YQsEyB,IAEzB,qBACE,sCACA,gCACA,4CACA,uCACA,oCACA,+BACA,wBACE,wBAEF,2BACE,0CACA,kCACA,4CAIJ,wBACE,8BAGF,iBACE,gBACA,oBACA,gBACA,uBACA,oBACA,qBACA,4BAGF,uBACE,qBAGF,kBACE,qBACA,4BACA,6BACE,mBACA,UACA,WACA,kBACA,cACA,WACA,kBACA,SACA,WAKN,qBACE,gBACA,oCACE,mBAIJ,oBACE,cAGF,0EACE,8CAGF,eACE,sBACA,iBAEA,4BACE,iBAEE,+CACE,oBAEF,oCACE,qBAMJ,8BACE,mBAMA,4CAxPF,gDA2PE,oDA3PF,+CA8PE,6CA9PF,gDAiQE,0CAjQF,+CAwQF,4BAxQE,iDA8QJ,aR3LE,MQ4L6B,QR3L7B,UQ2Le,QR1Lf,YQ0LwB,IAExB,oBACE,YAIJ,iBACE,mBACA,mBACE,gBAMF,iCACE,iBAIJ,kCACE,kBACE,sCACA,kCACA,kCACE,WACA,gBAIJ,oCR7OA,YQ8OiB,SR7OjB,aQ6OiB,SAEf,gBACA,4CACE,YAMN,kCACE,oBACE,6BAIJ,kCAGM,uDAvTJ,YACA,2BACA,aAsTmB,MArTnB,cAqT2B,MAEvB,qCACE,yCACA,wCACA,kCACA,+BAOR,kCACE,iBACE,eACA,gBACA,oBACA,sBCzVJ,KACE,mBACA,oBACA,mBACA,iBACA,iBACA,8CACA,uCACA,UACE,iBACA,eACA,gCCXJ,2DACE,WACA,UACA,UACA,qBACA,WACA,kBACA,uCAGF,UACE,sBAEA,oBACE,iBACA,kBACA,SAEA,2BACE,WACA,cACA,kBACA,0BACA,uBACA,kBACA,WACA,YACA,UACA,UACA,iBACA,gDACA,qCACA,6BACA,UAGF,sCACE,kBACA,SACA,6CACE,UAOJ,gBACE,iBACA,iBAEA,oBACE,mBACA,gBACA,uBAEA,sBAEE,mBACA,kBACA,UAIJ,+BACE,8CACA,iFAGF,uBAGE,cACA,YAGF,oCAGE,eACA,aAIJ,mDACE,cAGF,6CACE,aAIJ,gBACE,mBACA,qBACA,sBACE,aACA,kBAEA,gCAEE,WACA,qBACA,kBACA,0BACA,uBACA,kBACA,UACA,WACA,WACA,YACA,UACA,yCACA,6BACA,UAGJ,oBACE,cACA,8BACA,kBACA,kBACA,aACA,kBACA,cAMN,kCACE,UACE,iBACA,aACE,kBCvIN,2EACE,WAGF,YACE,mBACA,yBACE,mBAIA,oCAEE,cAIJ,6BACE,iBACA,kBACA,kBAIA,yCACE,yBACA,0BAWN,kBACE,aACA,cACA,kBACA,kBACA,yBACA,wBACE,kBACA,aACA,WACA,0BAIJ,qBACE,wBACE,6CAIJ,QACE,6BACA,iCACA,yBC5DF,MACE,2BACA,2CAKA,qCACE,mBACA,gBAEA,qDACE,gBACA,UACA,WACA,kBACA,cACA,WACA,kBACA,UACA,mBAGF,yCAGE,iBAGF,qEACE,mBAKN,eACE,iBAGF,oBACE,kBAMA,iEAGE,mBAIJ,kCAIM,qDACE,eAEF,yCACE,mBACA,gBACA,wBC/DR,UACI,gBACA,sBACA,yBAEA,iBACI,yBACA,WACA,YACA,aACA,eACA,kBACA,eAGJ,uBACI,sBAGJ,wBACI,sBAIR,0BACE,yBAEA,iCACE,WAGF,uCACI,yBAGJ,wCACI,yBAIN,iBACI,aACA,iBACA,sBACA,gBAGJ,KACE,kCACA,SACE,kCAIJ,2CACE,KACE,qCACA,SACE,sCAMJ,cACE,aAEF,uBACE,cACA,iBACA,mBACA,eACA,iBAEF,cACE,uCAEF,kBACE,uCAIJ,eACE,aAIA,kBACE,UACA,aACA,SACA,cACA,cAEF,0BACE,kCACA,gBAEF,wBACE,YAKF,iCACE,YAKF,iCACE,YAKF,kCACE,aAIJ,0BACE,6CAGF,2BACE,8CAGF,yBACE,6CAGF,gBACE,mBACA,mBAGF,cACE,mBAGF,SACE,+CACA,YAGF,KACE,6CACA,YAGF,MACE,6CACA,YAGF,0BACE,2BACE,0BAIJ,0BACE,eACE","sourcesContent":["/*!\n * The styles for Jekyll theme Chirpy\n *\n * Chirpy v3.3.2 (https://github.com/cotes2020/jekyll-theme-chirpy)\n * © 2019 Cotes Chung\n * MIT Licensed\n */\n\n@import\n \"colors/light-typography\",\n \"colors/dark-typography\",\n\n \"addon/module\",\n \"addon/variables\",\n \"addon/syntax\",\n \"addon/commons\",\n\n \"layout/home\",\n \"layout/post\",\n \"layout/tags\",\n \"layout/archives\",\n \"layout/categories\",\n \"layout/category-tag\";\n","/*\n The common styles\n*/\n@import url('https://fonts.googleapis.com/css2?family=Lato&family=Source+Sans+Pro:wght@400;600;900&display=swap');\n\n@mixin mode-toggle($dark-mode: false) {\n @if $dark-mode {\n @include dark-scheme;\n\n .mode-toggle {\n transform: rotateY(180deg);\n }\n\n } @else {\n @include light-scheme;\n\n .mode-toggle {\n transform: none;\n }\n }\n\n}\n\nhtml:not([mode]),\nhtml[mode=light] {\n @include mode-toggle();\n}\n\nhtml[mode=dark] {\n @include mode-toggle(true);\n}\n\n@media (prefers-color-scheme: dark) {\n html:not([mode]),\n html[mode=dark] {\n @include mode-toggle(true);\n }\n\n html[mode=light] {\n @include mode-toggle();\n }\n}\n\n:root {\n font-size: 16px;\n}\n\nbody {\n line-height: 1.75rem;\n background: var(--body-bg);\n color: var(--text-color);\n -webkit-font-smoothing: antialiased;\n font-family: 'Source Sans Pro', 'Microsoft Yahei', sans-serif;\n}\n\n/* --- Typography --- */\n\nh1 {\n @extend %heading;\n\n font-size: 1.8rem;\n}\n\nh2 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.4rem;\n}\n\nh3 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.25rem;\n}\n\nh4 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.15rem;\n}\n\nh5 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.1rem;\n}\n\nol,\nul {\n ol,\n ul {\n margin-bottom: 1rem;\n }\n}\n\na {\n @extend %link-color;\n}\n\nimg {\n max-width: 100%;\n}\n\nblockquote {\n border-left: 5px solid var(--blockquote-border-color);\n padding-left: 1rem;\n color: var(--blockquote-text-color);\n}\n\nkbd {\n margin: 0 0.3rem;\n}\n\nfooter {\n position: absolute;\n bottom: 0;\n padding: 0 1rem;\n height: $footer-height;\n font-size: 0.8rem;\n color: #7a7b7d;\n background-color: var(--footer-bg-color);\n\n > div.d-flex {\n line-height: 1.2rem;\n width: 95%;\n max-width: 1045px;\n border-top: 1px solid var(--main-border-color);\n margin-bottom: 1rem;\n\n > div {\n width: 350px;\n }\n }\n\n a {\n color: var(--footer-link);\n &:link {\n @include no-text-decoration;\n }\n &:hover {\n @extend %link-hover;\n\n @include no-text-decoration;\n }\n }\n .footer-right {\n text-align: right;\n }\n}\n\n/* --- Panels --- */\n\n.access {\n top: 2rem;\n transition: top 0.2s ease-in-out;\n margin-right: 1.5rem;\n margin-top: 3rem;\n margin-bottom: 4rem;\n\n &:only-child {\n position: -webkit-sticky; /* Safari */\n position: sticky;\n }\n &.topbar-down {\n top: 6rem;\n }\n > div {\n padding-left: 1rem;\n border-left: 1px solid var(--main-border-color);\n &:not(:last-child) {\n margin-bottom: 4rem;\n }\n }\n span {\n @include panel-label;\n }\n .post-content {\n font-size: 0.9rem;\n }\n}\n\n#access-tags {\n > div.post-content > div {\n max-width: 80%;\n }\n\n .post-tag {\n display: inline-block;\n line-height: 1rem;\n font-size: 0.85rem;\n background: none;\n border: 1px solid var(--btn-border-color);\n border-radius: 0.8rem;\n padding: 0.3rem 0.5rem;\n margin: 0 0.35rem 0.5rem 0;\n &:hover {\n background-color: #2a408e;\n border-color: #2a408e;\n color: #fff;\n transition: none;\n }\n }\n}\n\n#access-lastmod {\n\n li {\n height: 1.8rem;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n list-style: none;\n }\n\n a {\n &:hover {\n @extend %link-hover;\n }\n\n @extend %no-bottom-border;\n\n color: #6c757d;\n }\n\n}\n\n.footnotes > ol {\n padding-left: 2rem;\n margin-top: 0.5rem;\n > li {\n &:not(:last-child) {\n margin-bottom: 0.3rem;\n }\n > p {\n margin-left: 0.25em;\n margin-top: 0;\n margin-bottom: 0;\n }\n // [scroll-focus] added by `smooth-scroll.js`\n &:target:not([scroll-focus]),\n &[scroll-focus=true] > p {\n background-color: var(--footnote-target-bg);\n width: fit-content;\n -webkit-transition: background-color 1.5s ease-in-out; // Safari prior 6.1\n transition: background-color 1.5s ease-in-out;\n }\n }\n}\n\n.footnote {\n @at-root a#{&} {\n @include ml-mr(1px);\n @include pl-pr(2px);\n\n border-bottom-style: none !important;\n -webkit-transition: background-color 1.5s ease-in-out; // Safari prior 6.1\n transition: background-color 1.5s ease-in-out;\n }\n\n // [scroll-focus] added by `smooth-scroll.js`\n @at-root sup:target:not([scroll-focus]),\n sup[scroll-focus=true] > a#{&} {\n background-color: var(--footnote-target-bg);\n }\n}\n\n.reversefootnote {\n @at-root a#{&} {\n font-size: 0.6rem;\n position: absolute;\n line-height: 1;\n padding-top: 0.5em;\n margin-left: 0.5em;\n border-bottom-style: none !important;\n }\n}\n\n.post {\n h1 {\n margin-top: 3rem;\n margin-bottom: 1rem;\n }\n em { /* MarkDown italic */\n padding-right: 0.2rem;\n }\n a:hover {\n code {\n @extend %link-hover;\n }\n }\n}\n\n/* --- Begin of Markdown table style --- */\n\n.table-wrapper { // it will be created by Liquid\n overflow-x: auto;\n margin-bottom: 1.5rem;\n\n > table {\n min-width: 100%;\n overflow-x: auto;\n border-spacing: 0;\n\n thead {\n border-bottom: solid 2px rgba(210, 215, 217, 0.75);\n th {\n @extend %table-cell;\n }\n }\n\n tbody {\n tr {\n border-bottom: 1px solid var(--tb-border-color);\n &:nth-child(2n) {\n background-color: var(--tb-even-bg);\n }\n &:nth-child(2n + 1) {\n background-color: var(--tb-odd-bg);\n }\n td {\n @extend %table-cell;\n }\n }\n }\n }\n}\n\n/* --- post --- */\n\n.pageviews .fa-spinner {\n font-size: 80%;\n}\n\n.post-meta {\n font-size: 0.85rem;\n word-spacing: 1px;\n a {\n @extend %link-color;\n @extend %link-underline;\n &:not(:last-child) {\n margin-right: 2px;\n }\n &:hover {\n @extend %link-hover;\n }\n }\n}\n\n.post-content {\n line-height: 1.8;\n margin-top: 2rem;\n overflow-wrap: break-word;\n word-wrap: break-word;\n\n img[data-src] {\n margin: 0.5rem 0;\n }\n\n @mixin img-caption {\n + em {\n display: block;\n text-align: center;\n font-style: normal;\n font-size: 80%;\n padding: 0;\n color: #6d6c6c;\n }\n }\n\n @mixin img($caption: false) {\n\n > img[data-src] {\n @if $caption {\n @include img-caption;\n }\n\n &.left {\n float: left;\n margin: 0.75rem 1rem 1rem 0;\n }\n\n &.right {\n float: right;\n margin: 0.75rem 0 1rem 1rem;\n }\n\n }\n }\n\n a {\n blockquote & {\n color: var(--link-color);\n }\n\n &:not(.img-hyperlink) {\n @extend %link-color;\n @extend %link-underline;\n &:hover {\n @extend %link-hover;\n }\n }\n\n &.img-hyperlink {\n @include img;\n @include img-caption;\n }\n }\n\n > p {\n > img[data-src]:not(.normal):not(.left):not(.right) {\n @include align-center;\n }\n }\n\n p {\n @include img(true);\n\n font-size: 1.08rem;\n }\n\n ul {\n // attribute 'hide-bullet' was added by liquid\n .task-list-item[hide-bullet] {\n list-style-type: none;\n\n > i { // checkbox icon\n margin: 0 0.4rem 0.2rem -1.4rem;\n vertical-align: middle;\n color: var(--checkbox-color);\n &.checked {\n color: var(--checkbox-checked-color);\n }\n }\n\n }\n\n input[type=checkbox] {\n margin: 0 0.5rem 0.2rem -1.3rem;\n vertical-align: middle;\n }\n\n } // ul\n\n > ol,\n > ul {\n padding-left: 2rem;\n\n li {\n + li {\n margin-top: 0.3rem;\n }\n\n ol,\n ul { // sub list\n padding-left: 2rem;\n margin-top: 0.3rem;\n }\n }\n\n }\n\n > ol {\n li {\n padding-left: 0.25em;\n }\n }\n\n dl > dd {\n margin-left: 1rem;\n }\n\n} // .post-content\n\n.tag:hover {\n @extend %tag-hover;\n}\n\n.post-tag {\n display: inline-block;\n min-width: 2rem;\n text-align: center;\n background: var(--tag-bg);\n border-radius: 0.3rem;\n padding: 0 0.4rem;\n color: inherit;\n line-height: 1.3rem;\n\n &:not(:last-child) {\n margin-right: 0.2rem;\n }\n\n &:hover {\n @extend %tag-hover;\n\n border-bottom: none;\n text-decoration: none;\n color: #d2603a;\n }\n}\n\n/* --- buttons --- */\n.btn-lang {\n border: 1px solid !important;\n padding: 1px 3px;\n border-radius: 3px;\n color: var(--link-color);\n &:focus {\n box-shadow: none;\n }\n}\n\n/* --- Effects classes --- */\n\n.semi-bold {\n font-weight: 600 !important;\n}\n\n.loaded {\n display: block !important;\n\n @at-root .d-flex#{&} {\n display: flex !important;\n }\n}\n\n.unloaded {\n display: none !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.hidden {\n visibility: hidden !important;\n}\n\n.flex-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n}\n\n.btn-box-shadow {\n box-shadow: 0 0 8px 0 var(--btn-box-shadow) !important;\n}\n\n.topbar-up {\n top: -3rem !important; /* same as topbar height. */\n}\n\n.no-text-decoration {\n @include no-text-decoration;\n}\n\n.tooltip-inner { /* Overrided BS4 Tooltip */\n font-size: 0.7rem;\n max-width: 220px;\n text-align: left;\n}\n\n.disabled {\n color: rgb(206, 196, 196);\n pointer-events: auto;\n cursor: not-allowed;\n}\n\n.hide-border-bottom {\n border-bottom: none !important;\n}\n\n.input-focus {\n box-shadow: none;\n border-color: var(--input-focus-border-color) !important;\n background: center !important;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n}\n\n/* --- sidebar layout --- */\n\n$tab-count: 5 !default;\n\n$sidebar-display: \"sidebar-display\";\n\n#sidebar {\n @include pl-pr(0);\n\n position: fixed;\n top: 0;\n left: 0;\n height: 100%;\n overflow-y: auto;\n width: $sidebar-width-medium;\n z-index: 99;\n background: var(--sidebar-bg);\n\n a {\n @include sidebar-links;\n\n &:hover {\n @include no-text-decoration;\n\n color: #fff;\n }\n }\n\n #avatar {\n &:hover > a {\n border-color: #fff;\n }\n\n > a {\n display: block;\n width: 6rem;\n height: 6rem;\n border-radius: 50%;\n border: 2px solid #b6b6b6;\n overflow: hidden;\n transform: translateZ(0); // fixed the zoom in Safari\n -webkit-transition: border-color 0.35s ease-in-out;\n -moz-transition: border-color 0.35s ease-in-out;\n transition: border-color 0.35s ease-in-out;\n }\n\n img {\n width: 100%;\n height: 100%;\n -webkit-transition: transform 0.5s;\n -moz-transition: transform 0.5s;\n transition: transform 0.5s;\n\n &:hover {\n -ms-transform: scale(1.2);\n -moz-transform: scale(1.2);\n -webkit-transform: scale(1.2);\n transform: scale(1.2);\n }\n }\n } // #avatar\n\n .site-title {\n a {\n font-weight: 900;\n font-size: 1.5rem;\n letter-spacing: 0.5px;\n }\n }\n\n .site-subtitle {\n font-size: 95%;\n color: #828282;\n line-height: 1.2rem;\n word-spacing: 1px;\n margin: 0.5rem 1.5rem 0.5rem 1.5rem;\n min-height: 3rem; // avoid vertical shifting in multi-line words\n user-select: none;\n }\n\n .nav-link {\n border-radius: 0;\n font-size: 0.95rem;\n font-weight: 600;\n letter-spacing: 1px;\n display: table-cell;\n vertical-align: middle;\n }\n\n .nav-item {\n text-align: center;\n display: table;\n height: $tab-height;\n &:hover {\n .nav-link {\n color: #f8f9facf;\n }\n }\n &.active {\n .nav-link {\n color: #fcfcfc;\n }\n }\n }\n\n ul {\n height: $tab-height * $tab-count;\n margin-bottom: 2rem;\n padding-left: 0;\n\n li {\n width: 100%;\n\n &:last-child {\n $cursor-width: 3px;\n\n a {\n position: relative;\n left: $cursor-width / 2;\n width: 100%;\n }\n\n &::after { // the cursor\n display: table;\n visibility: hidden;\n content: \"\";\n position: relative;\n right: 1px;\n width: $cursor-width;\n height: $tab-cursor-height;\n border-radius: 1px;\n background-color: var(--nav-cursor-color);\n pointer-events: none;\n }\n }\n } // li\n\n @mixin fix-cursor($top) {\n top: $top;\n visibility: visible;\n }\n\n @for $i from 1 through $tab-count {\n $offset: $tab-count - $i;\n $top: -$offset * $tab-height + $tab-cursor-height / 2;\n\n @if $i < $tab-count {\n > li.active:nth-child(#{$i}),\n > li.nav-item:nth-child(#{$i}):hover {\n ~li:last-child::after {\n @include fix-cursor($top);\n }\n }\n } @else {\n > li.active:nth-child(#{$i}):last-child::after,\n > li.nav-item:nth-child(#{$i}):last-child:hover::after {\n @include fix-cursor($top);\n }\n }\n\n } // @for\n\n } // ul\n\n .sidebar-bottom {\n font-size: 1.2rem;\n margin-bottom: 2.1rem;\n\n @include ml-mr(auto);\n @include pl-pr(1rem);\n\n %icon {\n width: 2.4rem;\n text-align: center;\n }\n\n a {\n @extend %icon;\n }\n\n #mode-toggle-wrapper {\n @extend %icon;\n\n i {\n @include sidebar-links;\n\n margin: 0;\n font-size: 1.05rem;\n text-align: center;\n position: relative;\n bottom: 1px;\n }\n\n }\n\n .icon-border {\n background-color: #525354;\n content: \"\";\n width: 3px;\n height: 3px;\n border-radius: 50%;\n position: relative;\n top: 12px;\n }\n\n a:hover,\n #mode-toggle-wrapper i:hover {\n color: #fff;\n }\n\n } // .sidebar-bottom\n\n} // #sidebar\n\n@media (hover: hover) {\n #sidebar ul > li:last-child::after {\n -webkit-transition: top 0.5s ease;\n -moz-transition: top 0.5s ease;\n -o-transition: top 0.5s ease;\n transition: top 0.5s ease;\n }\n}\n\n.profile-wrapper {\n margin-top: 2rem;\n width: 100%;\n}\n\n#search-result-wrapper {\n display: none;\n height: 100%;\n overflow: auto;\n .post-content {\n margin-top: 2rem;\n }\n}\n\n/* --- top-bar --- */\n\n#topbar-wrapper {\n height: $topbar-height;\n position: fixed;\n top: 0;\n left: 260px; /* same as sidebar width */\n right: 0;\n transition: top 0.2s ease-in-out;\n z-index: 50;\n border-bottom: 1px solid rgba(0, 0, 0, 0.07);\n box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.05);\n background-color: var(--topbar-wrapper-bg);\n}\n\n#topbar {\n i { // icons\n color: #999;\n }\n\n #breadcrumb {\n font-size: 1rem;\n color: gray;\n padding-left: 0.5rem;\n\n a:hover {\n @extend %link-hover;\n }\n\n span {\n &:not(:last-child) {\n &::after {\n content: \"›\";\n padding: 0 0.3rem;\n }\n }\n }\n }\n} // #topbar\n\n#sidebar-trigger,\n#search-trigger {\n display: none;\n}\n\n#search-wrapper {\n display: flex;\n width: 95%;\n border-radius: 1rem;\n border: 1px solid var(--search-wrapper-bg);\n background: var(--search-wrapper-bg);\n padding: 0 0.5rem;\n i {\n z-index: 2;\n font-size: 0.9rem;\n color: var(--search-icon-color);\n }\n .fa-times-circle { /* button 'clean up' */\n visibility: hidden;\n }\n}\n\n#search-cancel { /* 'Cancel' link */\n color: var(--link-color);\n margin-left: 1rem;\n display: none;\n}\n\n#search-input {\n background: center;\n border: 0;\n border-radius: 0;\n padding: 0.18rem 0.3rem;\n color: var(--text-color);\n font-size: 95%;\n\n &:focus {\n box-shadow: none;\n background: center;\n &.form-control {\n &::-webkit-input-placeholder { @include input-placeholder; }\n &::-moz-placeholder { @include input-placeholder; }\n &:-ms-input-placeholder { @include input-placeholder; }\n &::placeholder { @include input-placeholder; }\n }\n }\n}\n\n#search-hints {\n display: none;\n\n .post-tag {\n display: inline-block;\n line-height: 1rem;\n font-size: 1rem;\n background: var(--search-tag-bg);\n border: none;\n padding: 0.5rem;\n margin: 0 1rem 1rem 0;\n &::before {\n content: \"#\";\n color: var(--text-muted-color);\n padding-right: 0.2rem;\n }\n }\n}\n\n#search-results {\n padding-bottom: 6rem;\n a {\n &:hover {\n @extend %link-hover;\n }\n\n @extend %link-color;\n @extend %no-bottom-border;\n @extend %heading;\n\n font-size: 1.4rem;\n line-height: 2.5rem;\n }\n\n > div {\n width: 100%;\n\n &:not(:last-child) {\n margin-bottom: 1rem;\n }\n\n i { // icons\n color: #818182;\n margin-right: 0.15rem;\n font-size: 80%;\n }\n\n > p {\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n }\n }\n} // #search-results\n\n#topbar-title {\n display: none;\n font-size: 1.1rem;\n font-weight: 600;\n font-family: sans-serif;\n color: var(--topbar-text-color);\n text-align: center;\n width: 70%;\n overflow: hidden;\n text-overflow: ellipsis;\n word-break: keep-all;\n white-space: nowrap;\n}\n\n#page {\n .categories,\n #tags,\n #archives {\n a:not(:hover) {\n @extend %no-bottom-border;\n }\n }\n}\n\n#mask {\n display: none;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n height: 100%;\n width: 100%;\n z-index: 1;\n\n @at-root [#{$sidebar-display}] & {\n display: block !important;\n }\n}\n\n/* --- main wrapper --- */\n\n#main-wrapper {\n background-color: var(--main-wrapper-bg);\n position: relative;\n min-height: 100vh;\n padding-bottom: $footer-height;\n\n @include pl-pr(0);\n}\n\n#main > div.row:first-child > div {\n &:nth-child(1),\n &:nth-child(2) {\n margin-top: $topbar-height; /* same as the height of topbar */\n }\n &:first-child {\n /* 3rem for topbar, 6rem for footer */\n min-height: calc(100vh - #{$topbar-height} - #{$footer-height} - #{$post-extend-min-height});\n }\n}\n\n#post-wrapper {\n min-height: calc(\n 100vh - #{$topbar-height} - #{$footer-height} - #{$post-extend-min-height}) !important;\n}\n\n#topbar-wrapper.row,\n#main > .row,\n#search-result-wrapper > .row {\n @include ml-mr(0);\n}\n\n/* --- button back-to-top --- */\n\n#back-to-top {\n display: none;\n z-index: 1;\n cursor: pointer;\n position: fixed;\n background: var(--button-bg);\n color: var(--btn-backtotop-color);\n height: 2.6em;\n width: 2.7em;\n border-radius: 50%;\n border: 1px solid var(--btn-backtotop-border-color);\n transition: 0.2s ease-out;\n -webkit-transition: 0.2s ease-out;\n}\n\n#back-to-top:hover {\n transform: translate3d(0, -5px, 0);\n -webkit-transform: translate3d(0, -5px, 0);\n}\n\n/* --- Responsive Design --- */\n\n@media all and (max-width: 576px) {\n\n $footer-height: 6rem; // overwrite\n\n #main > div.row:first-child > div:first-child {\n min-height: calc(100vh - #{$topbar-height} - #{$footer-height});\n }\n\n #post-wrapper {\n min-height: calc(\n 100vh - #{$topbar-height} - #{$footer-height} - #{$post-extend-min-height}) !important;\n h1 {\n margin-top: 2.2rem;\n font-size: 1.55rem;\n }\n }\n\n #avatar > a {\n width: 5rem;\n height: 5rem;\n }\n\n .site-subtitle {\n @include ml-mr(1.8rem);\n }\n\n #main-wrapper {\n padding-bottom: $footer-height;\n }\n\n footer {\n height: $footer-height;\n > div.d-flex {\n width: 100%;\n padding: 1.5rem 0;\n margin-bottom: 0.3rem;\n flex-wrap: wrap;\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .footer-left,\n .footer-right {\n text-align: center;\n }\n }\n\n}\n\n/* Hide Sidebar and TOC */\n@media all and (max-width: 830px) {\n %slide {\n -webkit-transition: transform 0.4s ease;\n transition: transform 0.4s ease;\n }\n\n html,\n body {\n overflow-x: hidden;\n }\n\n .footnotes ol > li {\n padding-top: 3.5rem;\n margin-top: -3.2rem;\n &:first-child {\n margin-top: -3.5rem;\n }\n }\n\n [#{$sidebar-display}] {\n\n #sidebar {\n transform: translateX(0);\n }\n\n #topbar-wrapper,\n #main-wrapper {\n transform: translateX(#{$sidebar-width-medium});\n }\n\n }\n\n #sidebar {\n @extend %slide;\n\n transform: translateX(-#{$sidebar-width-medium}); // hide\n -webkit-transform: translateX(-#{$sidebar-width-medium});\n\n .cursor {\n -webkit-transition: none;\n -moz-transition: none;\n transition: none;\n }\n }\n\n #main-wrapper {\n @extend %slide;\n\n padding-top: $topbar-height;\n }\n\n #search-result-wrapper {\n width: 100%;\n }\n\n #breadcrumb,\n #search-wrapper {\n display: none;\n }\n\n #topbar-wrapper {\n @extend %slide;\n\n left: 0;\n }\n\n .topbar-up {\n top: 0 !important;\n }\n\n #main > div.row:first-child > div:nth-child(1),\n #main > div.row:first-child > div:nth-child(2) {\n margin-top: 0;\n }\n\n #topbar-title,\n #sidebar-trigger,\n #search-trigger {\n display: block;\n }\n\n #search-wrapper {\n &.loaded ~ a {\n margin-right: 1rem;\n }\n .fa-times-circle {\n right: 5.2rem;\n }\n }\n\n #search-input {\n margin-left: 0;\n width: 95%;\n }\n\n #search-result-wrapper .post-content {\n letter-spacing: 0;\n }\n\n #search-hints {\n display: block;\n padding: 0 1rem;\n }\n\n #tags {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n\n #page h1.dynamic-title {\n display: none;\n ~ .post-content {\n margin-top: 3rem;\n }\n }\n\n}\n\n@media all and (min-width: 577px) and (max-width: 1199px) {\n footer > .d-flex > div {\n width: 312px;\n }\n}\n\n/* Sidebar visible */\n@media all and (min-width: 831px) {\n /* Solved jumping scrollbar */\n html {\n overflow-y: scroll;\n }\n\n #main-wrapper {\n margin-left: $sidebar-width-medium;\n }\n\n .profile-wrapper {\n margin-top: 3rem;\n }\n\n #search-wrapper {\n width: 22%;\n min-width: 150px;\n }\n\n #search-result-wrapper {\n margin-top: 3rem;\n }\n\n div.post-content .table-wrapper > table {\n min-width: 70%;\n }\n\n /* button 'back-to-Top' position */\n #back-to-top {\n bottom: 5.5rem;\n right: 1.2rem;\n }\n\n .topbar-up {\n box-shadow: none !important;\n }\n\n #topbar-title {\n text-align: left;\n }\n\n footer > div.d-flex {\n width: 92%;\n }\n\n}\n\n/* iPad 9.7\" horizontal */\n@media all and (min-width: 992px) and (max-width: 1024px) {\n #main-wrapper .col-lg-11 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 96%;\n flex: 0 0 96%;\n max-width: 96%;\n }\n}\n\n/* Compact icons in sidebar & TOC hidden */\n@media all and (min-width: 832px) and (max-width: 1199px) {\n\n #sidebar {\n width: $sidebar-width-small;\n .sidebar-bottom {\n a,\n span {\n width: 2rem;\n }\n .icon-border {\n left: -3px;\n }\n }\n }\n\n #topbar-wrapper {\n left: 210px;\n }\n\n #search-results > div {\n max-width: 700px;\n }\n\n .site-title {\n font-size: 1.3rem;\n margin-left: 0 !important;\n }\n\n .site-subtitle {\n @include ml-mr(1rem);\n\n font-size: 90%;\n }\n\n #main-wrapper {\n margin-left: 210px;\n }\n\n #breadcrumb {\n width: 65%;\n overflow: hidden;\n text-overflow: ellipsis;\n word-break: keep-all;\n white-space: nowrap;\n }\n\n}\n\n/* Pannel hidden */\n@media all and (max-width: 1199px) {\n #panel-wrapper {\n display: none;\n }\n\n #topbar {\n padding: 0;\n }\n\n #main > div.row {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n}\n\n/* --- desktop mode, both sidebar and panel are visible --- */\n\n@media all and (min-width: 1200px) {\n\n #main > div.row > div.col-xl-8 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n padding-left: 3%;\n }\n\n #topbar {\n padding: 0;\n max-width: 1070px;\n }\n\n #panel-wrapper {\n max-width: $panel-max-width;\n }\n\n #back-to-top {\n bottom: 6.5rem;\n right: 4.3rem;\n }\n\n #search-input {\n -webkit-transition: all 0.3s ease-in-out;\n transition: all 0.3s ease-in-out;\n }\n\n #search-results > div {\n width: 46%;\n &:nth-child(odd) {\n margin-right: 1.5rem;\n }\n &:nth-child(even) {\n margin-left: 1.5rem;\n }\n &:last-child:nth-child(odd) {\n position: relative;\n right: 24.3%;\n }\n }\n\n .post-content p {\n font-size: 1.03rem;\n }\n\n footer > div.d-felx {\n width: 85%;\n }\n\n}\n\n@media all and (min-width: 1400px) {\n\n #main > div.row {\n padding-left: calc((100% - #{$main-content-max-width}) / 2);\n > div.col-xl-8 {\n max-width: 850px;\n }\n }\n\n #search-result-wrapper {\n padding-right: 2rem;\n > div {\n max-width: 1110px;\n }\n }\n\n #search-wrapper .fa-times-circle {\n right: 2.6rem;\n }\n\n}\n\n@media all and (min-width: 1400px) and (max-width: 1650px) {\n #topbar {\n padding-right: 2rem;\n }\n}\n\n@media all and (min-width: 1650px) {\n\n #breadcrumb {\n padding-left: 0;\n }\n\n #main > div.row > div.col-xl-8 {\n padding-left: 0;\n > div:first-child {\n padding-left: 0.55rem !important;\n padding-right: 1.9rem !important;\n }\n }\n\n #main-wrapper {\n margin-left: $sidebar-width-large;\n }\n\n #panel-wrapper {\n margin-left: calc((100% - #{$main-content-max-width}) / 10);\n }\n\n #topbar-wrapper {\n left: $sidebar-width-large;\n }\n\n #topbar {\n max-width: #{$main-content-max-width};\n }\n\n #search-wrapper {\n margin-right: 3%;\n }\n\n #sidebar {\n width: $sidebar-width-large;\n\n .profile-wrapper {\n margin-top: 4rem;\n margin-bottom: 1rem;\n\n &.text-center {\n text-align: left !important;\n }\n\n %profile-ml {\n margin-left: 4.5rem;\n }\n\n #avatar {\n @extend %profile-ml;\n\n > a {\n width: 6.2rem;\n height: 6.2rem;\n &.mx-auto {\n margin-left: 0 !important;\n }\n }\n }\n\n .site-title {\n @extend %profile-ml;\n\n a {\n font-size: 1.7rem;\n letter-spacing: 1px;\n }\n }\n\n .site-subtitle {\n @extend %profile-ml;\n\n word-spacing: 0;\n margin-top: 0.3rem;\n }\n\n } // .profile-wrapper (min-width: 1650px)\n\n ul {\n padding-left: 2.5rem;\n\n > li:last-child {\n > a {\n position: static;\n }\n }\n\n .nav-item {\n text-align: left;\n\n .nav-link {\n > span {\n letter-spacing: 3px;\n }\n\n > i {\n @include icon-round(1.65rem);\n\n line-height: 1.5rem;\n font-size: 0.6rem;\n padding-top: 1px;\n padding-left: 1px;\n position: relative;\n bottom: 1px;\n\n &.unloaded {\n display: inline-block !important;\n }\n }\n }\n\n }\n }\n\n .sidebar-bottom {\n padding-left: 3.5rem;\n width: 100%;\n\n &.justify-content-center {\n -webkit-box-pack: start !important;\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n\n a {\n font-size: 1rem;\n width: 3rem;\n }\n\n i {\n @include icon-round(2rem);\n\n padding-top: 0.44rem;\n margin-top: .7rem; // multi line space\n bottom: 0;\n }\n\n #mode-toggle-wrapper {\n width: 3rem;\n\n i {\n top: 11px;\n }\n }\n\n .icon-border {\n top: 26px;\n }\n\n } // .sidebar-bottom\n\n } // #sidebar\n\n footer > div.d-flex {\n width: 87%;\n max-width: 1140px;\n }\n\n #search-result-wrapper {\n > div {\n max-width: #{$main-content-max-width};\n }\n }\n\n} // min-width: 1650px\n\n@media all and (min-width: 1700px) {\n #topbar-wrapper {\n /* 100% - 350px - (1920px - 350px); */\n padding-right: calc(100% - #{$sidebar-width-large} - (1920px - #{$sidebar-width-large}));\n }\n\n #topbar {\n max-width: calc(#{$main-content-max-width} + 20px);\n }\n\n #main > div.row {\n padding-left: calc((100% - #{$main-content-max-width} - 2%) / 2);\n }\n\n #panel-wrapper {\n margin-left: 3%;\n }\n\n footer {\n padding-left: 0;\n padding-right: calc(100% - #{$sidebar-width-large} - 1180px);\n }\n\n #back-to-top {\n right: calc(100% - 1920px + 15rem);\n }\n\n}\n\n@media (min-width: 1920px) {\n #main > div.row {\n padding-left: 190px;\n }\n\n #search-result-wrapper {\n padding-right: calc(100% - #{$sidebar-width-large} - 1180px);\n }\n\n #panel-wrapper {\n margin-left: 41px;\n }\n}\n","/*\n* Mainly scss modules, only imported to `assets/css/main.scss`\n*/\n\n/* ---------- scss placeholder --------- */\n\n%heading {\n color: var(--heading-color);\n font-weight: 400;\n font-family: 'Lato', 'Microsoft Yahei', sans-serif;\n}\n\n%tag-hover {\n background: var(--tag-hover);\n transition: background 0.35s ease-in-out;\n}\n\n%table-cell {\n padding: 0.4rem 1rem;\n font-size: 95%;\n white-space: nowrap;\n}\n\n%link-hover {\n color: #d2603a !important;\n border-bottom: 1px solid #d2603a;\n text-decoration: none;\n}\n\n%link-color {\n color: var(--link-color);\n}\n\n%link-underline {\n border-bottom: 1px solid var(--link-underline-color);\n}\n\n%no-bottom-border {\n border-bottom: none;\n}\n\n%section {\n #post-wrapper & {\n line-height: 1.2;\n margin-bottom: 1rem;\n }\n}\n\n%anchor {\n padding-top: 3.5rem;\n margin-top: -2.5rem;\n}\n\n/* ---------- scss mixin --------- */\n\n@mixin no-text-decoration {\n text-decoration: none;\n}\n\n@mixin sidebar-links($color: rgba(255, 255, 255, 0.5)) {\n color: $color;\n transition: color 0.35s ease-in-out;\n user-select: none;\n}\n\n@mixin icon-round($diameter) {\n border: 1px solid;\n border-radius: 50%;\n width: $diameter;\n height: $diameter;\n}\n\n@mixin ml-mr($value) {\n margin-left: $value;\n margin-right: $value;\n}\n\n@mixin pl-pr($val) {\n padding-left: $val;\n padding-right: $val;\n}\n\n@mixin input-placeholder {\n opacity: 0.6;\n}\n\n@mixin semi-bold {\n font-weight: 600;\n}\n\n@mixin label($font-size: 1rem, $font-weight: 600, $color: var(--label-color)) {\n color: $color;\n font-size: $font-size;\n font-weight: $font-weight;\n}\n\n@mixin panel-label {\n @include label(inherit);\n\n display: block;\n line-height: 1.2;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n margin-top: 0;\n margin-bottom: 0;\n letter-spacing: -0.02em;\n}\n\n@mixin align-center {\n position: relative;\n left: 50%;\n -webkit-transform: translateX(-50%);\n -ms-transform: translateX(-50%);\n transform: translateX(-50%);\n}\n","/*\n* The syntax highlight.\n*/\n\n@import \"colors/light-syntax\";\n@import \"colors/dark-syntax\";\n\nhtml:not([mode]),\nhtml[mode=light] {\n @include light-syntax;\n}\n\nhtml[mode=dark] {\n @include dark-syntax;\n}\n\n@media (prefers-color-scheme: dark) {\n html:not([mode]),\n html[mode=dark] {\n @include dark-syntax;\n }\n\n html[mode=light] {\n @include light-syntax;\n }\n}\n\n/* -- Codes Snippet -- */\n\n%code-snippet-bg {\n background: var(--highlight-bg-color);\n}\n\n%code-snippet-radius {\n border-radius: 6px;\n}\n\n%code-snippet-padding {\n padding: 1.5rem;\n}\n\n$code-font-size: 0.85rem;\n\ndiv > pre {\n @extend %code-snippet-bg;\n @extend %code-snippet-radius;\n @extend %code-snippet-padding;\n}\n\n.highlighter-rouge {\n @extend %code-snippet-bg;\n @extend %code-snippet-radius;\n\n color: var(--highlighter-rouge-color);\n margin-top: 0.5rem;\n margin-bottom: 1.2em; /* Override BS Inline-code style */\n}\n\n.highlight {\n @extend %code-snippet-radius;\n @extend %code-snippet-bg;\n\n @at-root figure#{&} {\n @extend %code-snippet-bg;\n }\n\n overflow: auto;\n .lineno {\n margin-left: 0.2rem;\n padding-right: 0.5rem;\n min-width: 2.2rem;\n text-align: right;\n color: var(--highlight-lineno-color);\n border-right: 1px solid var(--highlight-lineno-border-color);\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n -o-user-select: none;\n user-select: none;\n }\n pre {\n margin-bottom: 0;\n font-size: $code-font-size;\n line-height: 1.4rem;\n word-wrap: normal; /* Fixed Safari overflow-x */\n }\n table {\n padding: 0;\n border: 0;\n td pre {\n overflow: visible; /* Fixed iOS safari overflow-x */\n word-break: normal; /* Fixed iOS safari linenos code break */\n }\n }\n td {\n padding: 0;\n border: 0;\n }\n} //.highlight\n\ncode {\n -webkit-hyphens: none;\n -ms-hyphens: none;\n -moz-hyphens: none;\n hyphens: none;\n\n &.highlighter-rouge {\n font-size: $code-font-size;\n padding: 3px 5px;\n border-radius: 4px;\n background-color: var(--inline-code-bg);\n }\n\n @at-root a > &.highlighter-rouge {\n padding-bottom: 0; // show link's underlinke\n color: inherit;\n }\n\n @at-root a:hover > &.highlighter-rouge {\n border-bottom: none;\n }\n\n blockquote &.highlighter-rouge {\n color: inherit;\n }\n}\n\ntd.rouge-code {\n padding: 1.5rem 1.5rem 1.5rem 1rem;\n\n // Prevent some browser extends from\n // changing the URL string of code block.\n a {\n color: inherit !important;\n border-bottom: none !important;\n pointer-events: none;\n }\n\n}\n\n/* Hide line numbers for default, console, and terminal code snippets */\ndiv {\n &[class^='highlighter-rouge'],\n &.language-plaintext.highlighter-rouge,\n &.language-console.highlighter-rouge,\n &.language-terminal.highlighter-rouge {\n pre.lineno {\n display: none;\n }\n td.rouge-code {\n @extend %code-snippet-padding;\n }\n }\n}\n\ndiv[class^='language-']::before {\n content: attr(lang);\n position: absolute;\n right: 2rem;\n margin-top: 3px;\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--highlight-lineno-color);\n text-transform: uppercase;\n}\n\n@media (min-width: 768px) {\n div[class^='language-']::before {\n right: 3.1rem;\n }\n}\n\n@media (min-width: 1650px) {\n div[class^='language-']::before {\n right: 3.5rem;\n }\n}\n","/*\n * The syntax light mode code snippet colors.\n */\n\n@mixin light-syntax {\n /* see: */\n .highlight .hll { background-color: #ffffcc; }\n .highlight .c { color: #999988; font-style: italic; } /* Comment */\n .highlight .err { color: #a61717; background-color: #e3d2d2; } /* Error */\n .highlight .k { color: #000000; font-weight: bold; } /* Keyword */\n .highlight .o { color: #000000; font-weight: bold; } /* Operator */\n .highlight .cm { color: #999988; font-style: italic; } /* Comment.Multiline */\n .highlight .cp { color: #999999; font-weight: bold; font-style: italic; } /* Comment.Preproc */\n .highlight .c1 { color: #999988; font-style: italic; } /* Comment.Single */\n .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } /* Comment.Special */\n .highlight .gd { color: #d01040; background-color: #ffdddd; } /* Generic.Deleted */\n .highlight .ge { color: #000000; font-style: italic; } /* Generic.Emph */\n .highlight .gr { color: #aa0000; } /* Generic.Error */\n .highlight .gh { color: #999999; } /* Generic.Heading */\n .highlight .gi { color: #008080; background-color: #ddffdd; } /* Generic.Inserted */\n .highlight .go { color: #888888; } /* Generic.Output */\n .highlight .gp { color: #555555; } /* Generic.Prompt */\n .highlight .gs { font-weight: bold; } /* Generic.Strong */\n .highlight .gu { color: #aaaaaa; } /* Generic.Subheading */\n .highlight .gt { color: #aa0000; } /* Generic.Traceback */\n .highlight .kc { color: #000000; font-weight: bold; } /* Keyword.Constant */\n .highlight .kd { color: #000000; font-weight: bold; } /* Keyword.Declaration */\n .highlight .kn { color: #000000; font-weight: bold; } /* Keyword.Namespace */\n .highlight .kp { color: #000000; font-weight: bold; } /* Keyword.Pseudo */\n .highlight .kr { color: #000000; font-weight: bold; } /* Keyword.Reserved */\n .highlight .kt { color: #445588; font-weight: bold; } /* Keyword.Type */\n .highlight .m { color: #009999; } /* Literal.Number */\n .highlight .s { color: #d01040; } /* Literal.String */\n .highlight .na { color: #008080; } /* Name.Attribute */\n .highlight .nb { color: #0086b3; } /* Name.Builtin */\n .highlight .nc { color: #445588; font-weight: bold; } /* Name.Class */\n .highlight .no { color: #008080; } /* Name.Constant */\n .highlight .nd { color: #3c5d5d; font-weight: bold; } /* Name.Decorator */\n .highlight .ni { color: #800080; } /* Name.Entity */\n .highlight .ne { color: #990000; font-weight: bold; } /* Name.Exception */\n .highlight .nf { color: #990000; font-weight: bold; } /* Name.Function */\n .highlight .nl { color: #990000; font-weight: bold; } /* Name.Label */\n .highlight .nn { color: #555555; } /* Name.Namespace */\n .highlight .nt { color: #000080; } /* Name.Tag */\n .highlight .nv { color: #008080; } /* Name.Variable */\n .highlight .ow { color: #000000; font-weight: bold; } /* Operator.Word */\n .highlight .w { color: #bbbbbb; } /* Text.Whitespace */\n .highlight .mf { color: #009999; } /* Literal.Number.Float */\n .highlight .mh { color: #009999; } /* Literal.Number.Hex */\n .highlight .mi { color: #009999; } /* Literal.Number.Integer */\n .highlight .mo { color: #009999; } /* Literal.Number.Oct */\n .highlight .sb { color: #d01040; } /* Literal.String.Backtick */\n .highlight .sc { color: #d01040; } /* Literal.String.Char */\n .highlight .sd { color: #d01040; } /* Literal.String.Doc */\n .highlight .s2 { color: #d01040; } /* Literal.String.Double */\n .highlight .se { color: #d01040; } /* Literal.String.Escape */\n .highlight .sh { color: #d01040; } /* Literal.String.Heredoc */\n .highlight .si { color: #d01040; } /* Literal.String.Interpol */\n .highlight .sx { color: #d01040; } /* Literal.String.Other */\n .highlight .sr { color: #009926; } /* Literal.String.Regex */\n .highlight .s1 { color: #d01040; } /* Literal.String.Single */\n .highlight .ss { color: #990073; } /* Literal.String.Symbol */\n .highlight .bp { color: #999999; } /* Name.Builtin.Pseudo */\n .highlight .vc { color: #008080; } /* Name.Variable.Class */\n .highlight .vg { color: #008080; } /* Name.Variable.Global */\n .highlight .vi { color: #008080; } /* Name.Variable.Instance */\n .highlight .il { color: #009999; } /* Literal.Number.Integer.Long */\n\n /* --- custom light colors --- */\n --highlight-bg-color: #f7f7f7;\n --highlighter-rouge-color: #505050;\n --highlight-lineno-color: #c2c6cc;\n --highlight-lineno-border-color: #e9ecef;\n --inline-code-bg: #f3f3f3;\n} // light-syntax\n","/*\n * The syntax dark mode styles.\n */\n\n@mixin dark-syntax {\n /* ----- My styles ------ */\n --highlight-bg-color: #252525;\n --highlighter-rouge-color: #de6b18;\n --highlight-lineno-color: #6c6c6d;\n --highlight-lineno-border-color: #303435;\n --inline-code-bg: #272822;\n\n .highlight {\n .gp { color: #818c96; }\n }\n\n pre { color: #bfbfbf; } /* override Bootstrap */\n kbd { background-color: black; }\n\n /* syntax highlight colors from https://raw.githubusercontent.com/jwarby/pygments-css/master/monokai.css */\n .highlight pre { background-color: var(--highlight-bg-color); }\n .highlight .hll { background-color: var(--highlight-bg-color); }\n .highlight .c { color: #75715e; } /* Comment */\n .highlight .err { color: #960050; background-color: #1e0010; } /* Error */\n .highlight .k { color: #66d9ef; } /* Keyword */\n .highlight .l { color: #ae81ff; } /* Literal */\n .highlight .n { color: #f8f8f2; } /* Name */\n .highlight .o { color: #f92672; } /* Operator */\n .highlight .p { color: #f8f8f2; } /* Punctuation */\n .highlight .cm { color: #75715e; } /* Comment.Multiline */\n .highlight .cp { color: #75715e; } /* Comment.Preproc */\n .highlight .c1 { color: #75715e; } /* Comment.Single */\n .highlight .cs { color: #75715e; } /* Comment.Special */\n .highlight .ge { color: inherit; font-style: italic; } /* Generic.Emph */\n .highlight .gs { font-weight: bold; } /* Generic.Strong */\n .highlight .kc { color: #66d9ef; } /* Keyword.Constant */\n .highlight .kd { color: #66d9ef; } /* Keyword.Declaration */\n .highlight .kn { color: #f92672; } /* Keyword.Namespace */\n .highlight .kp { color: #66d9ef; } /* Keyword.Pseudo */\n .highlight .kr { color: #66d9ef; } /* Keyword.Reserved */\n .highlight .kt { color: #66d9ef; } /* Keyword.Type */\n .highlight .ld { color: #e6db74; } /* Literal.Date */\n .highlight .m { color: #ae81ff; } /* Literal.Number */\n .highlight .s { color: #e6db74; } /* Literal.String */\n .highlight .na { color: #a6e22e; } /* Name.Attribute */\n .highlight .nb { color: #f8f8f2; } /* Name.Builtin */\n .highlight .nc { color: #a6e22e; } /* Name.Class */\n .highlight .no { color: #66d9ef; } /* Name.Constant */\n .highlight .nd { color: #a6e22e; } /* Name.Decorator */\n .highlight .ni { color: #f8f8f2; } /* Name.Entity */\n .highlight .ne { color: #a6e22e; } /* Name.Exception */\n .highlight .nf { color: #a6e22e; } /* Name.Function */\n .highlight .nl { color: #f8f8f2; } /* Name.Label */\n .highlight .nn { color: #f8f8f2; } /* Name.Namespace */\n .highlight .nx { color: #a6e22e; } /* Name.Other */\n .highlight .py { color: #f8f8f2; } /* Name.Property */\n .highlight .nt { color: #f92672; } /* Name.Tag */\n .highlight .nv { color: #f8f8f2; } /* Name.Variable */\n .highlight .ow { color: #f92672; } /* Operator.Word */\n .highlight .w { color: #f8f8f2; } /* Text.Whitespace */\n .highlight .mf { color: #ae81ff; } /* Literal.Number.Float */\n .highlight .mh { color: #ae81ff; } /* Literal.Number.Hex */\n .highlight .mi { color: #ae81ff; } /* Literal.Number.Integer */\n .highlight .mo { color: #ae81ff; } /* Literal.Number.Oct */\n .highlight .sb { color: #e6db74; } /* Literal.String.Backtick */\n .highlight .sc { color: #e6db74; } /* Literal.String.Char */\n .highlight .sd { color: #e6db74; } /* Literal.String.Doc */\n .highlight .s2 { color: #e6db74; } /* Literal.String.Double */\n .highlight .se { color: #ae81ff; } /* Literal.String.Escape */\n .highlight .sh { color: #e6db74; } /* Literal.String.Heredoc */\n .highlight .si { color: #e6db74; } /* Literal.String.Interpol */\n .highlight .sx { color: #e6db74; } /* Literal.String.Other */\n .highlight .sr { color: #e6db74; } /* Literal.String.Regex */\n .highlight .s1 { color: #e6db74; } /* Literal.String.Single */\n .highlight .ss { color: #e6db74; } /* Literal.String.Symbol */\n .highlight .bp { color: #f8f8f2; } /* Name.Builtin.Pseudo */\n .highlight .vc { color: #f8f8f2; } /* Name.Variable.Class */\n .highlight .vg { color: #f8f8f2; } /* Name.Variable.Global */\n .highlight .vi { color: #f8f8f2; } /* Name.Variable.Instance */\n .highlight .il { color: #ae81ff; } /* Literal.Number.Integer.Long */\n .highlight .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */\n .highlight .gd { color: #f92672; background-color: #561c08; } /* Generic.Deleted & Diff Deleted */\n .highlight .gi { color: #a6e22e; background-color: #0b5858; } /* Generic.Inserted & Diff Inserted */\n}\n","/*\n * The syntax light mode typography colors\n */\n\n@mixin light-scheme {\n /* Common */\n --body-bg: #fafafa;\n --mask-bg: #c1c3c5;\n --main-wrapper-bg: white;\n --main-border-color: #f3f3f3;\n --btn-border-color: #e9ecef;\n --text-color: #4a4a4a;\n --heading-color: black;\n --blockquote-border-color: #eee;\n --blockquote-text-color: #9a9a9a;\n --link-color: #2a408e;\n --link-underline-color: #dee2e6;\n --text-muted-color: gray;\n --tb-odd-bg: #fbfcfd;\n --tb-border-color: #eaeaea;\n --button-bg: #fff;\n --btn-backtotop-color: #686868;\n --btn-backtotop-border-color: #f1f1f1;\n --btn-box-shadow: #eaeaea;\n --checkbox-color: #c5c5c5;\n --checkbox-checked-color: #07a8f7;\n\n /* Sidebar */\n --sidebar-bg: radial-gradient(\n circle,\n rgba(42, 30, 107, 1) 0%,\n rgba(35, 37, 46, 1) 100%);\n --nav-cursor-color: #fcfcfc;\n\n /* Topbar */\n --topbar-wrapper-bg: white;\n --topbar-text-color: rgb(78, 78, 78);\n --search-wrapper-bg: #f5f5f5;\n --search-tag-bg: #f8f9fa;\n --search-icon-color: #c2c6cc;\n --input-focus-border-color: var(--btn-border-color);\n\n /* Home page */\n --post-list-text-color: dimgray;\n --btn-patinator-text-color: #555555;\n --btn-paginator-hover-color: #e9ecef;\n --btn-active-bg: #2a408e;\n --btn-active-border-color: #007bff;\n --btn-text-color: #f8f8f8;\n --btn-paginator-border-color: #f1f1f1;\n --btn-paginator-shadow: #4b92d2;\n --pin-bg: #f5f5f5;\n --pin-color: #999fa4;\n\n /* Posts */\n --btn-share-hover-color: var(--link-color);\n --card-border-color: #f1f1f1;\n --card-box-shadow: rgba(234, 234, 234, 0.7686274509803922);\n --label-color: #616161;\n --relate-post-date: rgba(30, 55, 70, 0.4);\n --tag-bg: rgba(0, 0, 0, 0.075);\n --tag-border: #dee2e6;\n --tag-shadow: var(--btn-border-color);\n --tag-hover: rgb(222, 226, 230);\n --categories-hover-bg: var(--btn-border-color);\n --dash-color: silver;\n\n /* Archive */\n --timeline-color: rgba(0, 0, 0, 0.075);\n --timeline-node-bg: #c2c6cc;\n --timeline-year-dot-color: #ffffff;\n\n /* Footer */\n --footer-bg-color: #ffffff;\n --footnote-target-bg: lightcyan;\n --footer-link: #424242;\n} // light-scheme\n","/*\n * The main dark mode styles\n */\n\n@mixin dark-scheme {\n /* framework */\n --main-wrapper-bg: rgb(27, 27, 30);\n --body-bg: var(--main-wrapper-bg);\n --topbar-wrapper-bg: rgb(39, 40, 43);\n --search-wrapper-bg: rgb(34, 34, 39);\n --search-icon-color: rgb(100, 102, 105);\n --input-focus-border-color: rgb(112, 114, 115);\n --mask-bg: rgb(68, 69, 70);\n --footer-bg-color: var(--main-wrapper-bg);\n\n /* common color */\n --text-color: rgb(175, 176, 177);\n --heading-color: #cccccc;\n --text-muted-color: rgb(107, 116, 124);\n --link-color: rgb(138, 180, 248);\n --link-underline-color: rgb(82, 108, 150);\n --main-border-color: rgb(44, 45, 45);\n --button-bg: rgb(39, 40, 43);\n --blockquote-border-color: rgb(66, 66, 66);\n --blockquote-text-color: rgb(117, 117, 117);\n --btn-border-color: rgb(63, 65, 68);\n --btn-backtotop-color: var(--text-color);\n --btn-backtotop-border-color: var(--btn-border-color);\n --btn-box-shadow: var(--main-wrapper-bg);\n --card-header-bg: rgb(51, 50, 50);\n --label-color: rgb(108, 117, 125);\n --checkbox-color: rgb(118 120 121);\n --checkbox-checked-color: var(--link-color);\n\n /* Sidebar */\n --nav-cursor-color: rgb(183, 182, 182);\n --sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);\n\n /* Top Bar */\n --topbar-text-color: var(--text-color);\n\n /* Home page */\n --post-list-text-color: rgb(175, 176, 177);\n --btn-patinator-text-color: var(--text-color);\n --btn-paginator-hover-color: rgb(64, 65, 66);\n --btn-active-bg: rgba(28, 52, 94, 1);\n --btn-active-border-color: rgb(66, 94, 138);\n --btn-text-color: var(--text-color);\n --btn-paginator-border-color: var(--btn-border-color);\n --btn-paginator-shadow: var(--main-wrapper-bg);\n --pin-bg: rgb(34 35 37);\n --pin-color: inherit;\n\n /* Posts */\n --toc-highlight: rgb(116, 178, 243);\n --tag-bg: rgb(41, 40, 40);\n --tag-hover: rgb(43, 56, 62);\n --tb-odd-bg: rgba(42, 47, 53, 0.52); /* odd rows of the posts' table */\n --tb-even-bg: rgb(31, 31, 34); /* even rows of the posts' table */\n --tb-border-color: var(--tb-odd-bg);\n --footnote-target-bg: rgb(63, 81, 181);\n --btn-share-color: #6c757d;\n --btn-share-hover-color: #bfc1ca;\n --relate-post-date: var(--text-muted-color);\n --card-bg: rgb(39, 40, 43);\n --card-border-color: rgb(53, 53, 60);\n --card-box-shadow: var(--main-wrapper-bg);\n\n /* tags */\n --tag-border: rgb(59, 79, 88);\n --tag-shadow: rgb(32, 33, 33);\n --search-tag-bg: var(--tag-bg);\n --dash-color: rgb(63, 65, 68);\n\n /* categories */\n --categories-border: rgb(64, 66, 69);\n --categories-hover-bg: rgb(73, 75, 76);\n\n /* archives */\n --timeline-node-bg: rgb(150, 152, 156);\n --timeline-color: rgb(63, 65, 68);\n --timeline-year-dot-color: var(--timeline-color);\n\n /* Footer */\n --footer-link: rgb(171, 171, 171);\n\n .post-content img {\n filter: brightness(90%);\n }\n\n hr {\n border-color: var(--main-border-color);\n }\n\n /* posts' toc, override BS */\n nav[data-toggle=toc] .nav-link.active,\n nav[data-toggle=toc] .nav-link.active:focus,\n nav[data-toggle=toc] .nav-link.active:hover,\n nav[data-toggle=toc] .nav > li > a:focus,\n nav[data-toggle=toc] .nav > li > a:hover {\n color: var(--toc-highlight) !important;\n border-left-color: var(--toc-highlight) !important;\n }\n\n /* categories */\n .categories.card,\n .list-group-item {\n background-color: var(--card-bg);\n }\n\n .categories {\n .card-header {\n background-color: var(--card-header-bg);\n }\n .list-group-item {\n border-left: none;\n border-right: none;\n padding-left: 2rem;\n border-color: var(--categories-border);\n &:last-child {\n border-bottom-color: var(--card-bg);\n }\n }\n }\n\n #archives li:nth-child(odd) {\n background-image: linear-gradient(\n to left,\n rgb(26, 26, 30),\n rgb(39, 39, 45),\n rgb(39, 39, 45),\n rgb(39, 39, 45),\n rgb(26, 26, 30));\n }\n\n} // dark-scheme\n","/*\n* Mainly scss variables\n*/\n\n/* --- ↓ width and height ---- */\n\n$tab-height: 3.2rem;\n$tab-cursor-height: 1.6rem;\n\n$sidebar-width-small: 210px;\n$sidebar-width-medium: 260px;\n$sidebar-width-large: 350px;\n\n$topbar-height: 3rem;\n\n$footer-height: 5rem;\n\n$main-content-max-width: 1150px;\n\n$panel-max-width: 300px;\n\n$post-extend-min-height: 35rem;\n","/*\n Style for Homepage\n*/\n\n.pagination {\n font-size: 1rem;\n a:hover {\n text-decoration: none;\n }\n\n .page-item {\n .page-link {\n color: var(--btn-patinator-text-color);\n width: 2.5rem;\n height: 2.5rem;\n padding: 0;\n text-align: center;\n display: -webkit-box;\n display: flex;\n -webkit-box-pack: center;\n justify-content: center;\n -webkit-box-align: center;\n align-items: center;\n border-radius: 50%;\n border: 1px solid var(--btn-paginator-border-color);\n font-family: 'Lato', sans-serif;\n background-color: var(--button-bg);\n &:hover {\n background-color: var(--btn-paginator-hover-color);\n }\n }\n &.active {\n .page-link {\n background-color: var(--btn-active-bg);\n border-color: var(--btn-active-border-color);\n box-shadow: 0 0 8px 0 var(--btn-paginator-shadow) !important;\n color: var(--btn-text-color);\n }\n }\n &.disabled {\n cursor: not-allowed;\n .page-link {\n color: rgba(108, 117, 125, 0.57);\n border-color: var(--btn-paginator-border-color);\n background-color: var(--button-bg);\n }\n }\n &:first-child .page-link,\n &:last-child .page-link {\n border-radius: 50%;\n }\n &:not(:last-child) {\n margin-right: 0.7rem;\n }\n } // .page-item\n\n} // .pagination\n\n#post-list {\n margin-top: 1rem;\n padding-right: 0.5rem;\n\n .post-preview {\n padding-top: 1.5rem;\n padding-bottom: 1rem;\n border-bottom: 1px solid var(--main-border-color);\n\n a:hover {\n @extend %link-hover;\n }\n\n h1 {\n font-size: 1.4rem;\n margin: 0;\n }\n\n .post-meta {\n i {\n font-size: 0.73rem;\n }\n span:not(:last-child) {\n margin-right: 1.2rem;\n }\n }\n\n .post-content {\n margin-top: 0.6rem;\n margin-bottom: 0.6rem;\n color: var(--post-list-text-color);\n > p {\n /* Make preview shorter on the homepage */\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n }\n\n .pin {\n > i {\n transform: rotate(45deg);\n padding-left: 3px;\n color: var(--pin-color);\n }\n > span {\n display: none;\n }\n }\n\n } // .post-preview\n\n} // #post-list\n\n/* Hide SideBar and TOC */\n@media all and (max-width: 830px) {\n .pagination {\n justify-content: center;\n }\n}\n\n/* Sidebar is visible */\n@media all and (min-width: 831px) {\n\n #post-list {\n margin-top: 1.5rem;\n .post-preview .post-meta {\n .pin {\n background: var(--pin-bg);\n border-radius: 5px;\n line-height: 1.4rem;\n height: 1.3rem;\n margin-top: 3px;\n padding-left: 1px;\n padding-right: 6px;\n\n > span {\n display: inline;\n }\n }\n }\n }\n\n .pagination {\n font-size: 0.85rem;\n .page-item .page-link {\n width: 2.2rem;\n height: 2.2rem;\n }\n }\n\n}\n\n/* Pannel hidden */\n@media all and (max-width: 1200px) {\n #post-list {\n padding-right: 0;\n }\n}\n","/*\n Post-specific style\n*/\n\n@mixin btn-sharing-color($light-color, $important: false) {\n @if $important {\n color: var(--btn-share-color, $light-color) !important;\n } @else {\n color: var(--btn-share-color, $light-color);\n }\n}\n\n@mixin btn-post-nav {\n width: 50%;\n position: relative;\n border-color: var(--btn-border-color);\n}\n\n@mixin dot($pl: 0.2rem, $pr: 0.4rem) {\n content: \"\\2022\";\n color: rgba(158, 158, 158, 0.8);\n padding-left: $pl;\n padding-right: $pr;\n}\n\n.timeago::before {\n content: attr(prefix);\n}\n\n#post-wrapper .post-meta {\n > div:nth-child(2) {\n > span:not(:first-child)::before {\n @include dot;\n }\n }\n\n #pv::after {\n content: \" views\";\n }\n\n .readtime::after {\n content: \" read\";\n }\n}\n\n.post-content {\n .preview-img {\n @include align-center;\n\n margin-top: 0;\n margin-bottom: 2.5rem !important;\n }\n}\n\n.post-tail-wrapper {\n margin-top: 6rem;\n border-bottom: 1px double var(--main-border-color);\n font-size: 0.85rem;\n}\n\n.post-tags {\n line-height: 2rem;\n}\n\n.post-navigation {\n padding-top: 3rem;\n padding-bottom: 4rem;\n\n .btn {\n @include btn-post-nav;\n\n color: var(--link-color);\n\n &:hover {\n background: #2a408e;\n color: #fff;\n border-color: #2a408e;\n }\n\n &.disabled {\n @include btn-post-nav;\n\n pointer-events: auto;\n cursor: not-allowed;\n background: none;\n color: gray;\n\n &:hover {\n border-color: none;\n }\n }\n\n &.btn-outline-primary.disabled:focus {\n box-shadow: none;\n }\n\n &::before {\n color: var(--text-muted-color);\n font-size: 0.65rem;\n text-transform: uppercase;\n content: attr(prompt);\n }\n\n &:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n left: 0.5px;\n }\n\n &:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n right: 0.5px;\n }\n }\n\n p {\n font-size: 1.1rem;\n line-height: 1.5rem;\n margin-top: 0.3rem;\n white-space: normal;\n }\n\n} // .post-navigation\n\n@keyframes fade-up {\n from {\n opacity: 0;\n position: relative;\n top: 2rem;\n }\n to {\n opacity: 1;\n position: relative;\n top: 0;\n }\n}\n\n#toc-wrapper {\n border-left: 1px solid rgba(158, 158, 158, 0.17);\n position: -webkit-sticky;\n position: sticky;\n top: 4rem;\n transition: top 0.2s ease-in-out;\n animation: fade-up 0.8s;\n &.topbar-down {\n top: 6rem;\n }\n > span {\n @include panel-label;\n }\n}\n\n#toc li > a {\n line-height: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n/* --- Related Posts --- */\n\n#related-posts {\n > h3 {\n @include label(1.1rem, 600);\n }\n .card {\n border-color: var(--card-border-color);\n background-color: var(--card-bg);\n box-shadow: 0 0 5px 0 var(--card-box-shadow);\n -webkit-transition: all 0.3s ease-in-out;\n -moz-transition: all 0.3s ease-in-out;\n transition: all 0.3s ease-in-out;\n h3 {\n color: var(--text-color);\n }\n &:hover {\n -webkit-transform: translate3d(0, -3px, 0);\n transform: translate3d(0, -3px, 0);\n box-shadow: 0 10px 15px -4px rgba(0, 0, 0, 0.15);\n }\n }\n\n .timeago {\n color: var(--relate-post-date);\n }\n\n p {\n font-size: 0.9rem;\n margin-bottom: 0.5rem;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n\n a:hover {\n text-decoration: none;\n }\n\n ul {\n list-style-type: none;\n padding-inline-start: 1.5rem;\n > li::before {\n background: #c2c9d4;\n width: 5px;\n height: 5px;\n border-radius: 1px;\n display: block;\n content: \"\";\n position: relative;\n top: 1rem;\n right: 1rem;\n }\n }\n}\n\n#post-extend-wrapper {\n min-height: 2rem;\n #disqus_thread {\n margin-bottom: 2rem;\n }\n}\n\n.post-tail-bottom a {\n color: inherit;\n}\n\n%btn-share-hovor {\n color: var(--btn-share-hover-color) !important;\n}\n\n.share-wrapper {\n vertical-align: middle;\n user-select: none;\n\n .share-icons {\n font-size: 1.2rem;\n a {\n &:not(:last-child) {\n margin-right: 0.25rem;\n }\n &:hover {\n text-decoration: none;\n > i {\n @extend %btn-share-hovor;\n }\n }\n }\n > i {\n padding-top: 0.35rem;\n &:hover {\n @extend %btn-share-hovor;\n }\n }\n .fab {\n &.fa-twitter {\n @include btn-sharing-color(rgba(29, 161, 242, 1));\n }\n &.fa-facebook-square {\n @include btn-sharing-color(rgb(66, 95, 156));\n }\n &.fa-telegram {\n @include btn-sharing-color(rgb(39, 159, 217));\n }\n &.fa-weibo {\n @include btn-sharing-color(rgb(229, 20, 43));\n }\n }\n\n } // .share-icons\n\n .fas.fa-link {\n @include btn-sharing-color(rgb(171, 171, 171));\n }\n\n} // .share-wrapper\n\n.share-label {\n @include label(inherit, 400, inherit);\n\n &::after {\n content: \":\";\n }\n}\n\n.license-wrapper {\n line-height: 1.2rem;\n > a {\n font-weight: 600;\n &:hover {\n @extend %link-hover;\n }\n }\n\n span:last-child {\n font-size: 0.85rem;\n }\n} // .license-wrapper\n\n@media all and (max-width: 576px) {\n .post-tail-bottom {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n >div:first-child {\n width: 100%;\n margin-top: 1rem;\n }\n }\n\n .post-content > div[class^='language-'] {\n @include ml-mr(-1.25rem);\n\n border-radius: 0;\n &::before { // the lang badge\n right: 1rem;\n }\n }\n\n}\n\n@media all and (max-width: 768px) {\n .post-content > p > img {\n max-width: calc(100% + 1rem);\n }\n}\n\n@media all and (min-width: 768px) {\n #post-wrapper {\n .post-meta {\n >div:not(:first-child)::before {\n @include dot(0.5rem, 0.2rem);\n }\n &.flex-column {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n }\n } // .post\n}\n\n/* Hide SideBar and TOC */\n@media all and (max-width: 830px) {\n .post-navigation {\n padding-left: 0;\n padding-right: 0;\n margin-left: -.5rem;\n margin-right: -.5rem;\n }\n}\n","/*\n Styles for Tab Tags\n*/\n\n.tag {\n border-radius: 0.7em;\n padding: 6px 8px 7px;\n margin-right: 0.8rem;\n line-height: 3rem;\n letter-spacing: 0;\n border: 1px solid var(--tag-border) !important;\n box-shadow: 0 0 3px 0 var(--tag-shadow);\n span {\n margin-left: 0.6em;\n font-size: 0.7em;\n font-family: 'Oswald', sans-serif;\n }\n}\n","/*\n Style for Archives\n*/\n\n%date-timeline {\n content: \"\";\n width: 4px;\n left: 75px;\n display: inline-block;\n float: left;\n position: relative;\n background-color: var(--timeline-color);\n}\n\n#archives {\n letter-spacing: 0.03rem;\n\n span.lead {\n font-size: 1.5rem;\n position: relative;\n left: 8px;\n\n &::after { /* Year dot */\n content: \"\";\n display: block;\n position: relative;\n -webkit-border-radius: 50%;\n -moz-border-radius: 50%;\n border-radius: 50%;\n width: 12px;\n height: 12px;\n top: -26px;\n left: 63px;\n border: 3px solid;\n background-color: var(--timeline-year-dot-color);\n border-color: var(--timeline-node-bg);\n box-shadow: 0 0 2px 0 #c2c6cc;\n z-index: 1;\n }\n\n &:not(:first-child) {\n position: relative;\n left: 4px;\n &::after {\n left: 67px;\n }\n }\n\n } // #archives span.lead\n\n ul {\n li {\n font-size: 1.1rem;\n line-height: 3rem;\n\n div {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n\n a {\n /* post title in Archvies */\n margin-left: 2.5rem;\n position: relative;\n top: 0.1rem;\n }\n }\n\n &:nth-child(odd) {\n background-color: var(--main-wrapper-bg, #fff);\n background-image: linear-gradient(to left, #fff, #fbfbfb, #fbfbfb, #fbfbfb, #fff);\n }\n\n &::after {\n @extend %date-timeline;\n\n height: 2.8rem;\n top: -1.3rem;\n }\n\n &:first-child::before {\n @extend %date-timeline;\n\n height: 3.06rem;\n top: -1.61rem;\n }\n }\n\n &:not(:last-child) > li:last-child::after {\n height: 3.4rem;\n }\n\n &:last-child > li:last-child::after {\n display: none;\n }\n } // #archives ul\n\n .date {\n white-space: nowrap;\n display: inline-block;\n &.month {\n width: 1.4rem;\n text-align: center;\n\n ~ a::before {\n /* A dot for Month and Day */\n content: \"\";\n display: inline-block;\n position: relative;\n -webkit-border-radius: 50%;\n -moz-border-radius: 50%;\n border-radius: 50%;\n width: 8px;\n height: 8px;\n float: left;\n top: 1.35rem;\n left: 69px;\n background-color: var(--timeline-node-bg);\n box-shadow: 0 0 3px 0 #c2c6cc;\n z-index: 1;\n }\n }\n &.day {\n font-size: 85%;\n font-family: 'Lato', sans-serif;\n text-align: center;\n margin-right: -2px;\n width: 1.2rem;\n position: relative;\n left: -.15rem;\n }\n } // #archives .date\n\n} // #archives\n\n@media all and (max-width: 576px) {\n #archives {\n margin-top: -1rem;\n ul {\n letter-spacing: 0;\n }\n }\n}\n","/*\n Style for Tab Categories\n*/\n\n%category-icon-color {\n color: gray;\n}\n\n.categories {\n margin-bottom: 2rem;\n .card-header {\n padding-right: 12px;\n }\n\n i {\n &.far,\n &.fas {\n font-size: 86%; // fontawesome icons\n }\n }\n\n .list-group-item {\n border-left: none;\n border-right: none;\n padding-left: 2rem;\n > i {\n @extend %category-icon-color;\n }\n &:first-child {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n\n }\n\n .card-header > span > i:first-child {\n @extend %category-icon-color;\n }\n\n} // .categories\n\n.category-trigger {\n width: 1.7rem;\n height: 1.7rem;\n border-radius: 50%;\n text-align: center;\n color: #6c757d !important;\n > i.fas {\n position: relative;\n height: 0.7rem;\n width: 1rem;\n transition: 300ms ease all;\n }\n}\n\n@media (hover: hover) { // only works on desktop\n .category-trigger:hover {\n background-color: var(--categories-hover-bg);\n }\n}\n\n.rotate {\n -ms-transform: rotate(-90deg); /* IE 9 */\n -webkit-transform: rotate(-90deg); /* Safari 3-8 */\n transform: rotate(-90deg);\n}\n","/*\n Style for page Category and Tag\n*/\n\n.dash {\n margin: 0 .5rem .6rem .5rem;\n border-bottom: 2px dotted var(--dash-color);\n}\n\n#page-category,\n#page-tag {\n ul > li {\n line-height: 1.5rem;\n padding: 0.6rem 0;\n\n &::before { // dot\n background: #999;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n display: block;\n content: \"\";\n position: relative;\n top: 0.6rem;\n margin-right: 0.5rem;\n }\n\n > a { /* post's title */\n @extend %no-bottom-border;\n\n font-size: 1.1rem;\n }\n\n > span:last-child {\n white-space: nowrap;\n } /* post's date */\n }\n}\n\n#page-tag h1 > i { // tag icon\n font-size: 1.2rem;\n}\n\n#page-category h1 > i {\n font-size: 1.25rem;\n}\n\n#page-category,\n#page-tag,\n#access-lastmod {\n a:hover {\n @extend %link-hover;\n\n margin-bottom: -1px; // Avoid jumping\n }\n}\n\n@media all and (max-width: 576px) {\n #page-category,\n #page-tag {\n ul > li {\n &::before {\n margin: 0 .5rem;\n }\n > a {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n }\n }\n}\n","@import \"jekyll-theme-chirpy\";\n\n// add your style below\n// Code tab styling.\n.code-tab {\n overflow: hidden;\n border: 1px solid #ccc;\n background-color: #f1f1f1;\n\n button {\n background-color: inherit;\n float: left;\n border: none;\n outline: none;\n cursor: pointer;\n padding: 14px 16px;\n transition: 0.3s;\n }\n\n button:hover {\n background-color: #ddd;\n }\n\n button.active {\n background-color: #ccc;\n }\n}\n\nhtml[mode=\"dark\"] .code-tab {\n background-color: #303030;\n\n button {\n color: #fff;\n }\n\n button:hover {\n background-color: #242424;\n }\n\n button.active {\n background-color: #1c1c1c;\n }\n}\n\n.code-tabcontent {\n display: none;\n padding: 6px 12px;\n border: 1px solid #ccc;\n border-top: none;\n}\n\ncode {\n font-family: 'Fira Code', monospace;\n pre {\n font-family: 'Fira Code', monospace;\n }\n}\n\n@supports (font-variation-settings: normal) {\n code {\n font-family: 'Fira Code VF', monospace;\n pre {\n font-family: 'Fira Code VF', monospace;\n }\n }\n}\n\n.unlock {\n .show {\n display: none;\n }\n .button_unlock {\n display: block;\n font-size: larger;\n border-radius: 15px;\n border-width: 0;\n padding: 3px 10px;\n }\n .hint {\n background: #ffc107b0;\n }\n .solution {\n background: #1ab21ab0;\n }\n}\n\n.button_unlock {\n display: none;\n}\n\n.code_fold {\n button {\n padding: 0;\n background: 0;\n border: 0;\n color: #28a745;\n font-size: 1em;\n }\n button::before {\n font-family: \"Font Awesome 5 Free\";\n font-weight: 900;\n }\n button:focus {\n outline: 0px;\n }\n}\n\n.code_fold_closed{\n button::before {\n content: \"\\f0fe\";\n }\n}\n\n.code_fold_opened{\n button::before {\n content: \"\\f146\";\n }\n}\n\n.code_fold_closed {\n .code_fold_span {\n display: none;\n }\n}\n\n.highlight_backline_green {\n background-color: #19fa192c;\n}\n\n.highlight_backline_yellow {\n background-color: #e3fa192c;\n}\n\n.highlight_backline_blue {\n background-color: #325ef02c;\n}\n\ntd.rouge-gutter {\n vertical-align: top;\n padding-top: 1.5rem;\n}\n\ntd.rouge-code {\n vertical-align: top;\n}\n\n.neutral {\n background-color: #6666665b;\n padding: 2px;\n}\n\n.red {\n background-color: #ff30305b;\n padding: 2px;\n}\n\n.blue {\n background-color: #4842ff5b;\n padding: 2px;\n}\n\n@media (min-width: 1400px) {\n #main > div.row > div.col-xl-8 {\n max-width: max(60%, 850px);\n }\n}\n\n@media (min-width: 1700px) {\n #panel-wrapper {\n margin-left: calc((100% - 1150px)/10);\n }\n}\n\n"],"file":"style.css"} \ No newline at end of file diff --git a/assets/img/comp_assets/MCPC_Editorial.pdf b/assets/img/comp_assets/MCPC_Editorial.pdf new file mode 100644 index 0000000..4389ba1 Binary files /dev/null and b/assets/img/comp_assets/MCPC_Editorial.pdf differ diff --git a/assets/img/comp_assets/cardtrick.png b/assets/img/comp_assets/cardtrick.png new file mode 100644 index 0000000..23cac8b Binary files /dev/null and b/assets/img/comp_assets/cardtrick.png differ diff --git a/assets/img/comp_assets/catsanddogs.png b/assets/img/comp_assets/catsanddogs.png new file mode 100644 index 0000000..5c3cfda Binary files /dev/null and b/assets/img/comp_assets/catsanddogs.png differ diff --git a/assets/img/comp_assets/coffeecrisis.png b/assets/img/comp_assets/coffeecrisis.png new file mode 100644 index 0000000..46965c8 Binary files /dev/null and b/assets/img/comp_assets/coffeecrisis.png differ diff --git a/assets/img/comp_assets/cqLogo1.png b/assets/img/comp_assets/cqLogo1.png new file mode 100644 index 0000000..8b44a2f Binary files /dev/null and b/assets/img/comp_assets/cqLogo1.png differ diff --git a/assets/img/comp_assets/cybercrime.png b/assets/img/comp_assets/cybercrime.png new file mode 100644 index 0000000..1ddfa53 Binary files /dev/null and b/assets/img/comp_assets/cybercrime.png differ diff --git a/assets/img/comp_assets/diabolicaldefuser.png b/assets/img/comp_assets/diabolicaldefuser.png new file mode 100644 index 0000000..8225dae Binary files /dev/null and b/assets/img/comp_assets/diabolicaldefuser.png differ diff --git a/assets/img/comp_assets/diggerrykker.jpg b/assets/img/comp_assets/diggerrykker.jpg new file mode 100644 index 0000000..96d3529 Binary files /dev/null and b/assets/img/comp_assets/diggerrykker.jpg differ diff --git a/assets/img/comp_assets/fungus1.png b/assets/img/comp_assets/fungus1.png new file mode 100644 index 0000000..c14f67f Binary files /dev/null and b/assets/img/comp_assets/fungus1.png differ diff --git a/assets/img/comp_assets/fungus2.png b/assets/img/comp_assets/fungus2.png new file mode 100644 index 0000000..e6954bb Binary files /dev/null and b/assets/img/comp_assets/fungus2.png differ diff --git a/assets/img/comp_assets/gov_div1.png b/assets/img/comp_assets/gov_div1.png new file mode 100644 index 0000000..8adec8f Binary files /dev/null and b/assets/img/comp_assets/gov_div1.png differ diff --git a/assets/img/comp_assets/kangaroo_example.png b/assets/img/comp_assets/kangaroo_example.png new file mode 100644 index 0000000..839c228 Binary files /dev/null and b/assets/img/comp_assets/kangaroo_example.png differ diff --git a/assets/img/comp_assets/kickingkangaroo.png b/assets/img/comp_assets/kickingkangaroo.png new file mode 100644 index 0000000..584c4ea Binary files /dev/null and b/assets/img/comp_assets/kickingkangaroo.png differ diff --git a/assets/img/comp_assets/marble1.png b/assets/img/comp_assets/marble1.png new file mode 100644 index 0000000..971f519 Binary files /dev/null and b/assets/img/comp_assets/marble1.png differ diff --git a/assets/img/comp_assets/marble2.png b/assets/img/comp_assets/marble2.png new file mode 100644 index 0000000..df1db15 Binary files /dev/null and b/assets/img/comp_assets/marble2.png differ diff --git a/assets/img/comp_assets/meowmeow.png b/assets/img/comp_assets/meowmeow.png new file mode 100644 index 0000000..e212e9b Binary files /dev/null and b/assets/img/comp_assets/meowmeow.png differ diff --git a/assets/img/comp_assets/mocha.jpg b/assets/img/comp_assets/mocha.jpg new file mode 100644 index 0000000..d303c66 Binary files /dev/null and b/assets/img/comp_assets/mocha.jpg differ diff --git a/assets/img/comp_assets/playofthegame.png b/assets/img/comp_assets/playofthegame.png new file mode 100644 index 0000000..120b2c7 Binary files /dev/null and b/assets/img/comp_assets/playofthegame.png differ diff --git a/assets/img/comp_assets/rect10.png b/assets/img/comp_assets/rect10.png new file mode 100644 index 0000000..695fdfe Binary files /dev/null and b/assets/img/comp_assets/rect10.png differ diff --git a/assets/img/comp_assets/rect11.png b/assets/img/comp_assets/rect11.png new file mode 100644 index 0000000..86e3dbd Binary files /dev/null and b/assets/img/comp_assets/rect11.png differ diff --git a/assets/img/comp_assets/rect12.png b/assets/img/comp_assets/rect12.png new file mode 100644 index 0000000..73f1f80 Binary files /dev/null and b/assets/img/comp_assets/rect12.png differ diff --git a/assets/img/comp_assets/rect20.png b/assets/img/comp_assets/rect20.png new file mode 100644 index 0000000..f998d37 Binary files /dev/null and b/assets/img/comp_assets/rect20.png differ diff --git a/assets/img/comp_assets/rect22.png b/assets/img/comp_assets/rect22.png new file mode 100644 index 0000000..04571f6 Binary files /dev/null and b/assets/img/comp_assets/rect22.png differ diff --git a/assets/img/comp_assets/rect23.png b/assets/img/comp_assets/rect23.png new file mode 100644 index 0000000..d6b812b Binary files /dev/null and b/assets/img/comp_assets/rect23.png differ diff --git a/assets/img/comp_assets/rect24.png b/assets/img/comp_assets/rect24.png new file mode 100644 index 0000000..99c5948 Binary files /dev/null and b/assets/img/comp_assets/rect24.png differ diff --git a/assets/img/comp_assets/rect25.png b/assets/img/comp_assets/rect25.png new file mode 100644 index 0000000..a580760 Binary files /dev/null and b/assets/img/comp_assets/rect25.png differ diff --git a/assets/img/comp_assets/rock_shelf.png b/assets/img/comp_assets/rock_shelf.png new file mode 100644 index 0000000..6f76680 Binary files /dev/null and b/assets/img/comp_assets/rock_shelf.png differ diff --git a/assets/img/comp_assets/screechingbirds.png b/assets/img/comp_assets/screechingbirds.png new file mode 100644 index 0000000..6c08b2c Binary files /dev/null and b/assets/img/comp_assets/screechingbirds.png differ diff --git a/assets/img/comp_assets/scuttle_bug.jpg b/assets/img/comp_assets/scuttle_bug.jpg new file mode 100644 index 0000000..2569b07 Binary files /dev/null and b/assets/img/comp_assets/scuttle_bug.jpg differ diff --git a/assets/img/comp_assets/scuttle_example.png b/assets/img/comp_assets/scuttle_example.png new file mode 100644 index 0000000..cd445bc Binary files /dev/null and b/assets/img/comp_assets/scuttle_example.png differ diff --git a/assets/img/comp_assets/scuttle_sol.png b/assets/img/comp_assets/scuttle_sol.png new file mode 100644 index 0000000..55a28e0 Binary files /dev/null and b/assets/img/comp_assets/scuttle_sol.png differ diff --git a/assets/img/comp_assets/shortcircuit.png b/assets/img/comp_assets/shortcircuit.png new file mode 100644 index 0000000..04dfe02 Binary files /dev/null and b/assets/img/comp_assets/shortcircuit.png differ diff --git a/assets/img/comp_assets/stonks.png b/assets/img/comp_assets/stonks.png new file mode 100644 index 0000000..0d7c458 Binary files /dev/null and b/assets/img/comp_assets/stonks.png differ diff --git a/assets/img/comp_assets/tradingcards.png b/assets/img/comp_assets/tradingcards.png new file mode 100644 index 0000000..867ff39 Binary files /dev/null and b/assets/img/comp_assets/tradingcards.png differ diff --git a/assets/img/comp_assets/triangle1.png b/assets/img/comp_assets/triangle1.png new file mode 100644 index 0000000..c62501a Binary files /dev/null and b/assets/img/comp_assets/triangle1.png differ diff --git a/assets/img/favicons/android-icon-144x144.png b/assets/img/favicons/android-icon-144x144.png new file mode 100644 index 0000000..782cf3a Binary files /dev/null and b/assets/img/favicons/android-icon-144x144.png differ diff --git a/assets/img/favicons/android-icon-192x192.png b/assets/img/favicons/android-icon-192x192.png new file mode 100644 index 0000000..db2d8e6 Binary files /dev/null and b/assets/img/favicons/android-icon-192x192.png differ diff --git a/assets/img/favicons/android-icon-36x36.png b/assets/img/favicons/android-icon-36x36.png new file mode 100644 index 0000000..0b560f4 Binary files /dev/null and b/assets/img/favicons/android-icon-36x36.png differ diff --git a/assets/img/favicons/android-icon-48x48.png b/assets/img/favicons/android-icon-48x48.png new file mode 100644 index 0000000..7b01aa7 Binary files /dev/null and b/assets/img/favicons/android-icon-48x48.png differ diff --git a/assets/img/favicons/android-icon-72x72.png b/assets/img/favicons/android-icon-72x72.png new file mode 100644 index 0000000..a841bb8 Binary files /dev/null and b/assets/img/favicons/android-icon-72x72.png differ diff --git a/assets/img/favicons/android-icon-96x96.png b/assets/img/favicons/android-icon-96x96.png new file mode 100644 index 0000000..a9004f8 Binary files /dev/null and b/assets/img/favicons/android-icon-96x96.png differ diff --git a/assets/img/favicons/apple-icon-114x114.png b/assets/img/favicons/apple-icon-114x114.png new file mode 100644 index 0000000..59ea4a1 Binary files /dev/null and b/assets/img/favicons/apple-icon-114x114.png differ diff --git a/assets/img/favicons/apple-icon-120x120.png b/assets/img/favicons/apple-icon-120x120.png new file mode 100644 index 0000000..b44e035 Binary files /dev/null and b/assets/img/favicons/apple-icon-120x120.png differ diff --git a/assets/img/favicons/apple-icon-144x144.png b/assets/img/favicons/apple-icon-144x144.png new file mode 100644 index 0000000..782cf3a Binary files /dev/null and b/assets/img/favicons/apple-icon-144x144.png differ diff --git a/assets/img/favicons/apple-icon-152x152.png b/assets/img/favicons/apple-icon-152x152.png new file mode 100644 index 0000000..4bdb938 Binary files /dev/null and b/assets/img/favicons/apple-icon-152x152.png differ diff --git a/assets/img/favicons/apple-icon-180x180.png b/assets/img/favicons/apple-icon-180x180.png new file mode 100644 index 0000000..f1f5d35 Binary files /dev/null and b/assets/img/favicons/apple-icon-180x180.png differ diff --git a/assets/img/favicons/apple-icon-57x57.png b/assets/img/favicons/apple-icon-57x57.png new file mode 100644 index 0000000..0874717 Binary files /dev/null and b/assets/img/favicons/apple-icon-57x57.png differ diff --git a/assets/img/favicons/apple-icon-60x60.png b/assets/img/favicons/apple-icon-60x60.png new file mode 100644 index 0000000..c841d6e Binary files /dev/null and b/assets/img/favicons/apple-icon-60x60.png differ diff --git a/assets/img/favicons/apple-icon-72x72.png b/assets/img/favicons/apple-icon-72x72.png new file mode 100644 index 0000000..a841bb8 Binary files /dev/null and b/assets/img/favicons/apple-icon-72x72.png differ diff --git a/assets/img/favicons/apple-icon-76x76.png b/assets/img/favicons/apple-icon-76x76.png new file mode 100644 index 0000000..3dc2e8b Binary files /dev/null and b/assets/img/favicons/apple-icon-76x76.png differ diff --git a/assets/img/favicons/apple-icon-precomposed.png b/assets/img/favicons/apple-icon-precomposed.png new file mode 100644 index 0000000..8eb50f6 Binary files /dev/null and b/assets/img/favicons/apple-icon-precomposed.png differ diff --git a/assets/img/favicons/apple-icon.png b/assets/img/favicons/apple-icon.png new file mode 100644 index 0000000..8eb50f6 Binary files /dev/null and b/assets/img/favicons/apple-icon.png differ diff --git a/assets/img/favicons/browserconfig.xml b/assets/img/favicons/browserconfig.xml new file mode 100644 index 0000000..02c84b5 --- /dev/null +++ b/assets/img/favicons/browserconfig.xml @@ -0,0 +1 @@ + #ffffff diff --git a/assets/img/favicons/favicon-16x16.png b/assets/img/favicons/favicon-16x16.png new file mode 100644 index 0000000..5033c24 Binary files /dev/null and b/assets/img/favicons/favicon-16x16.png differ diff --git a/assets/img/favicons/favicon-32x32.png b/assets/img/favicons/favicon-32x32.png new file mode 100644 index 0000000..7f903ae Binary files /dev/null and b/assets/img/favicons/favicon-32x32.png differ diff --git a/assets/img/favicons/favicon-96x96.png b/assets/img/favicons/favicon-96x96.png new file mode 100644 index 0000000..a9004f8 Binary files /dev/null and b/assets/img/favicons/favicon-96x96.png differ diff --git a/assets/img/favicons/favicon.ico b/assets/img/favicons/favicon.ico new file mode 100644 index 0000000..d94950a Binary files /dev/null and b/assets/img/favicons/favicon.ico differ diff --git a/assets/img/favicons/manifest.json b/assets/img/favicons/manifest.json new file mode 100644 index 0000000..9610994 --- /dev/null +++ b/assets/img/favicons/manifest.json @@ -0,0 +1 @@ +{ "name": "Monash Code Binder", "short_name": "Monash Code Binder", "description": "A collection of algorithms, explanations and training problems", "icons": [ { "src": "/assets/img/favicons/android-icon-36x36.png", "sizes": "36x36", "type": "image/png", "density": "0.75" }, { "src": "/assets/img/favicons/android-icon-48x48.png", "sizes": "48x48", "type": "image/png", "density": "1.0" }, { "src": "/assets/img/favicons/android-icon-72x72.png", "sizes": "72x72", "type": "image/png", "density": "1.5" }, { "src": "/assets/img/favicons/android-icon-96x96.png", "sizes": "96x96", "type": "image/png", "density": "2.0" }, { "src": "/assets/img/favicons/android-icon-144x144.png", "sizes": "144x144", "type": "image/png", "density": "3.0" }, { "src": "/assets/img/favicons/android-icon-192x192.png", "sizes": "192x192", "type": "image/png", "density": "4.0" }], "start_url": "/index.html", "display": "fullscreen", "theme_color": "#2a1e6b", "background_color": "white" } diff --git a/assets/img/favicons/ms-icon-144x144.png b/assets/img/favicons/ms-icon-144x144.png new file mode 100644 index 0000000..782cf3a Binary files /dev/null and b/assets/img/favicons/ms-icon-144x144.png differ diff --git a/assets/img/favicons/ms-icon-150x150.png b/assets/img/favicons/ms-icon-150x150.png new file mode 100644 index 0000000..2b56368 Binary files /dev/null and b/assets/img/favicons/ms-icon-150x150.png differ diff --git a/assets/img/favicons/ms-icon-310x310.png b/assets/img/favicons/ms-icon-310x310.png new file mode 100644 index 0000000..f95a77a Binary files /dev/null and b/assets/img/favicons/ms-icon-310x310.png differ diff --git a/assets/img/favicons/ms-icon-70x70.png b/assets/img/favicons/ms-icon-70x70.png new file mode 100644 index 0000000..685290b Binary files /dev/null and b/assets/img/favicons/ms-icon-70x70.png differ diff --git a/assets/img/icpc.png b/assets/img/icpc.png new file mode 100644 index 0000000..2d4e7c0 Binary files /dev/null and b/assets/img/icpc.png differ diff --git a/assets/img/posts/cp1/grid1.png b/assets/img/posts/cp1/grid1.png new file mode 100644 index 0000000..9881e2c Binary files /dev/null and b/assets/img/posts/cp1/grid1.png differ diff --git a/assets/img/posts/cp1/grid2.png b/assets/img/posts/cp1/grid2.png new file mode 100644 index 0000000..3e2b66f Binary files /dev/null and b/assets/img/posts/cp1/grid2.png differ diff --git a/assets/img/posts/cp1/grid3.png b/assets/img/posts/cp1/grid3.png new file mode 100644 index 0000000..ae64288 Binary files /dev/null and b/assets/img/posts/cp1/grid3.png differ diff --git a/assets/img/posts/dp/calls.png b/assets/img/posts/dp/calls.png new file mode 100644 index 0000000..77f77e9 Binary files /dev/null and b/assets/img/posts/dp/calls.png differ diff --git a/assets/img/posts/dsless23/binary1.png b/assets/img/posts/dsless23/binary1.png new file mode 100644 index 0000000..cfbc0b2 Binary files /dev/null and b/assets/img/posts/dsless23/binary1.png differ diff --git a/assets/img/posts/dsless23/cuttingboard_rule1.png b/assets/img/posts/dsless23/cuttingboard_rule1.png new file mode 100644 index 0000000..4b10926 Binary files /dev/null and b/assets/img/posts/dsless23/cuttingboard_rule1.png differ diff --git a/assets/img/posts/dsless23/cuttingboard_rule2.png b/assets/img/posts/dsless23/cuttingboard_rule2.png new file mode 100644 index 0000000..0fe41d8 Binary files /dev/null and b/assets/img/posts/dsless23/cuttingboard_rule2.png differ diff --git a/assets/img/posts/dsless23/lights2.png b/assets/img/posts/dsless23/lights2.png new file mode 100644 index 0000000..a46e573 Binary files /dev/null and b/assets/img/posts/dsless23/lights2.png differ diff --git a/assets/img/posts/dsless23/misc2-1.PNG b/assets/img/posts/dsless23/misc2-1.PNG new file mode 100644 index 0000000..c40542d Binary files /dev/null and b/assets/img/posts/dsless23/misc2-1.PNG differ diff --git a/assets/img/posts/dsless23/misc2-2.PNG b/assets/img/posts/dsless23/misc2-2.PNG new file mode 100644 index 0000000..f891966 Binary files /dev/null and b/assets/img/posts/dsless23/misc2-2.PNG differ diff --git a/assets/img/posts/dsless23/parens.png b/assets/img/posts/dsless23/parens.png new file mode 100644 index 0000000..82f2f2e Binary files /dev/null and b/assets/img/posts/dsless23/parens.png differ diff --git a/assets/img/posts/dsless23/reciprocal.png b/assets/img/posts/dsless23/reciprocal.png new file mode 100644 index 0000000..aa79b21 Binary files /dev/null and b/assets/img/posts/dsless23/reciprocal.png differ diff --git a/assets/img/posts/dsless23/reciprocal_areas.png b/assets/img/posts/dsless23/reciprocal_areas.png new file mode 100644 index 0000000..7662656 Binary files /dev/null and b/assets/img/posts/dsless23/reciprocal_areas.png differ diff --git a/assets/img/posts/dsless23/reciprocal_fracs.png b/assets/img/posts/dsless23/reciprocal_fracs.png new file mode 100644 index 0000000..edfc238 Binary files /dev/null and b/assets/img/posts/dsless23/reciprocal_fracs.png differ diff --git a/assets/img/posts/lca/graph-0.png b/assets/img/posts/lca/graph-0.png new file mode 100644 index 0000000..9bb9c3d Binary files /dev/null and b/assets/img/posts/lca/graph-0.png differ diff --git a/assets/img/posts/lca/graph-1.png b/assets/img/posts/lca/graph-1.png new file mode 100644 index 0000000..3cb900b Binary files /dev/null and b/assets/img/posts/lca/graph-1.png differ diff --git a/assets/img/posts/lca/graph-2.png b/assets/img/posts/lca/graph-2.png new file mode 100644 index 0000000..34e4247 Binary files /dev/null and b/assets/img/posts/lca/graph-2.png differ diff --git a/assets/img/posts/lca/graph-3.png b/assets/img/posts/lca/graph-3.png new file mode 100644 index 0000000..684b32e Binary files /dev/null and b/assets/img/posts/lca/graph-3.png differ diff --git a/assets/img/posts/lca/graph-4.png b/assets/img/posts/lca/graph-4.png new file mode 100644 index 0000000..3b971a8 Binary files /dev/null and b/assets/img/posts/lca/graph-4.png differ diff --git a/assets/img/posts/lca/graph-5.png b/assets/img/posts/lca/graph-5.png new file mode 100644 index 0000000..9f951c0 Binary files /dev/null and b/assets/img/posts/lca/graph-5.png differ diff --git a/assets/img/posts/uf/find_reduce.png b/assets/img/posts/uf/find_reduce.png new file mode 100644 index 0000000..b02bed8 Binary files /dev/null and b/assets/img/posts/uf/find_reduce.png differ diff --git a/assets/img/posts/uf/merge_tree.png b/assets/img/posts/uf/merge_tree.png new file mode 100644 index 0000000..3f63f01 Binary files /dev/null and b/assets/img/posts/uf/merge_tree.png differ diff --git a/assets/index.html b/assets/index.html new file mode 100644 index 0000000..c31a99c --- /dev/null +++ b/assets/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/assets/js/code-tabs.js b/assets/js/code-tabs.js new file mode 100644 index 0000000..f8aad72 --- /dev/null +++ b/assets/js/code-tabs.js @@ -0,0 +1,50 @@ +$(document).ready(() => { + // Open all Python tabs by default. + var all_tablinks = document.getElementsByClassName("code-tablinks"); + for (var j = 0; j < all_tablinks.length; j++) { + all_tablinks[j].className = all_tablinks[j].className.replace(" active", ""); + } + + var all_tabcontent = document.getElementsByClassName("code-tabcontent"); + for (var j = 0; j < all_tabcontent.length; j++) { + all_tabcontent[j].style.display = "none"; + } + // Generate tooltips + // https://stackoverflow.com/a/48569235 + $(document).on('mouseover','.tooltip', function() { + var tooltipTrigger = $('[data-toggle="tooltip"][aria-describedby="' + $(this).attr('id') + '"'); + if(! $(tooltipTrigger).hasClass('active')) { + $(tooltipTrigger).tooltip('show').addClass('active'); + } + }); + $(document).on('mouseout','.tooltip-inner', function() { + $('[data-toggle="tooltip"].active').tooltip('hide').removeClass('active'); + }); + + $('[data-toggle="tooltip"]').tooltip() +}); + +function openCodeTab(evt, contentClass, tabName) { + // Declare all variables + var i, tabcontent, tablinks; + + if (document.getElementById(tabName).style.display == "block") { + document.getElementById(tabName).style.display = "none"; + evt.currentTarget.className = evt.currentTarget.className.replace(" active", ""); + return; + } + + tabcontent = document.getElementsByClassName(contentClass); + for (i = 0; i < tabcontent.length; i++) { + tabcontent[i].style.display = "none"; + } + + tablinks = document.getElementsByClassName(contentClass + "-link"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].className = tablinks[i].className.replace(" active", ""); + } + + // Show the current tab, and add an "active" class to the button that opened the tab + document.getElementById(tabName).style.display = "block"; + evt.currentTarget.className += " active"; +} diff --git a/assets/js/data/cache-list.js b/assets/js/data/cache-list.js new file mode 100644 index 0000000..05d459e --- /dev/null +++ b/assets/js/data/cache-list.js @@ -0,0 +1 @@ +const include = [ /* --- CSS --- */ '/assets/css/style.css', /* --- Javascripts --- */ '/assets/js/dist/home.min.js', '/assets/js/dist/page.min.js', '/assets/js/dist/post.min.js', '/assets/js/dist/categories.min.js', '/assets/js/data/search.json', '/app.js', '/sw.js', /* --- HTML --- */ '/index.html', '/404.html', '/categories/', '/tags/', '/archives/', '/about/', /* --- Icons --- */'/assets/img/favicons/favicon.ico', '/assets/img/favicons/apple-icon.png', '/assets/img/favicons/apple-icon-precomposed.png', '/assets/img/favicons/apple-icon-57x57.png', '/assets/img/favicons/apple-icon-60x60.png', '/assets/img/favicons/apple-icon-72x72.png', '/assets/img/favicons/apple-icon-76x76.png', '/assets/img/favicons/apple-icon-114x114.png', '/assets/img/favicons/apple-icon-120x120.png', '/assets/img/favicons/apple-icon-144x144.png', '/assets/img/favicons/apple-icon-152x152.png', '/assets/img/favicons/apple-icon-180x180.png', '/assets/img/favicons/android-icon-192x192.png', '/assets/img/favicons/favicon-32x32.png', '/assets/img/favicons/favicon-96x96.png', '/assets/img/favicons/favicon-16x16.png', '/assets/img/favicons/ms-icon-144x144.png', '/assets/img/favicons/manifest.json', '/assets/img/favicons/browserconfig.xml' ]; const exclude = ['/assets/js/data/pageviews.json', '/img.shields.io/' ]; diff --git a/assets/js/data/search.json b/assets/js/data/search.json new file mode 100644 index 0000000..23971b1 --- /dev/null +++ b/assets/js/data/search.json @@ -0,0 +1 @@ +[ { "title": "DataStructureLess Competition 2023 Editorial", "url": "/posts/dsless-editorial/", "categories": "Math", "tags": "", "date": "2023-12-28 18:00:00 +1100", "snippet": "Since the intention of the DataStructureLess Competition was to showcase some interesting/unique solve techniques, I thought it would be good to provide some editorial for all of the problems so everyone can see some of the cool stuff on offer.Each problem has been given a few hints, so you can h..." }, { "title": "Union Find / DSU", "url": "/posts/uf/", "categories": "Data Structures", "tags": "Difficulty 2", "date": "2021-12-15 12:00:00 +1100", "snippet": "Where is this useful?In many problems, translating into a graph structure can prove helpful, as we can describe our problem in very abstract terms.Once you’ve translated into this graph structure, often you might want to know whether two vertices are connected via a path, and if this is not the c..." }, { "title": "Challenge Problems - 2021 Sem 2, Contest 1", "url": "/posts/problems-21-s2-c1/", "categories": "Contests", "tags": "", "date": "2021-08-23 11:00:00 +1000", "snippet": "Sports LoansStatementAndrew is head of the sports club, and manages the inventory.Part of Andrew’s job is loaning footballs to people, and collecting those footballs once they have been used.At the start of the day, Andrew has \\(r\\) footballs in stock, and knows that \\(p+q\\) people will approach ..." }, { "title": "Least Common Ancestor (LCA)", "url": "/posts/lca/", "categories": "Data Structures, Trees", "tags": "Difficulty 3", "date": "2021-04-20 22:00:00 +1000", "snippet": "Where is this useful?The Least Common Ancestor (LCA) data structure is useful wherever you have a directed graph where every vertex has out-degree \\(\\leq 1\\). In more common terms, each vertex has a unique determined ‘parent’, or it is a root node, with no parent. The most common (and almost alwa..." }, { "title": "Primes and Factorization Techniques", "url": "/posts/factorization/", "categories": "Math", "tags": "Difficulty 3", "date": "2021-04-05 11:00:00 +1000", "snippet": "Why?Many number theoretic problems in competitive programming require analysing the factors or prime factors of a number.Here I’ll list a few techniques for finding these factors, and some techniques / properties involving the factors / prime factors of a number.PreliminariesFirst off, lets defin..." }, { "title": "Modular Arithmetic", "url": "/posts/mod/", "categories": "Math", "tags": "Difficulty 3", "date": "2021-03-29 18:40:00 +1100", "snippet": "What is it?Modular Arithmetic encompasses all sorts of theorems and optimizations surrounding the % operator in C and Python.As you’ll see in the related problems, modulo arithmetic is often tied together in a question regarding counting things (combinatorics) or probabilities.For those who haven..." }, { "title": "Dynamic Programming", "url": "/posts/dp/", "categories": "Data Structures", "tags": "Difficulty 2", "date": "2021-03-26 10:32:00 +1100", "snippet": "Why?Dynamic Programming (DP) is one of the most powerful tools you’ll come across in competitive programming.It normally turns up in about a third of all problems in a contest, in some form or another.What is it?Dynamic Programming is a general tactic centred around storing previous calculations ..." } ] diff --git a/assets/js/dist/categories.min.js b/assets/js/dist/categories.min.js new file mode 100644 index 0000000..f99b376 --- /dev/null +++ b/assets/js/dist/categories.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v3.3.2 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +function copyLink(e){e&&0!==e.length||(e=window.location.href);const o=$("");$("body").append(o),o.val(e).select(),document.execCommand("copy"),o.remove(),alert("Link copied successfully!")}$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$("input[type=checkbox]").addClass("unloaded"),$("input[type=checkbox][checked]").before(''),$("input[type=checkbox]:not([checked])").before('')}),$(function(){const e=$("#sidebar-trigger"),o=$("#search-trigger"),t=$("#search-cancel"),s=$("#search-cleaner"),a=$("#main"),l=$("#topbar-title"),n=$("#search-wrapper"),r=$("#search-result-wrapper"),d=$("#search-results"),c=$("#search-input"),i=$("#search-hints"),u=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),f={on(){e.addClass("unloaded"),l.addClass("unloaded"),o.addClass("unloaded"),n.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),n.removeClass("d-flex"),e.removeClass("unloaded"),l.removeClass("unloaded"),o.removeClass("unloaded")}},p=function(){let e=!1;return{on(){e||(u.block(),r.removeClass("unloaded"),a.addClass("unloaded"),e=!0)},off(){e&&(d.empty(),i.hasClass("unloaded")&&i.removeClass("unloaded"),r.addClass("unloaded"),s.removeClass("visible"),a.removeClass("unloaded"),u.release(),c.val(""),e=!1)},isVisible(){return e}}}();function h(){return t.hasClass("loaded")}o.click(function(){f.on(),p.on(),c.focus()}),t.click(function(){f.off(),p.off()}),c.focus(function(){n.addClass("input-focus")}),c.focusout(function(){n.removeClass("input-focus")}),c.on("keyup",function(e){8===e.keyCode&&""===c.val()?h()?i.removeClass("unloaded"):p.off():""!==c.val()&&(p.on(),s.hasClass("visible")||s.addClass("visible"),h()&&i.addClass("unloaded"))}),s.on("click",function(){c.val(""),h()?(i.removeClass("unloaded"),d.empty()):p.off(),c.focus(),s.removeClass("visible")})}),$(function(){var e=function(){const e="sidebar-display";let o=!1;const t=$("body");return{toggle(){!1===o?t.attr(e,""):t.removeAttr(e),o=!o}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const o=$("#topbar-wrapper"),t=$("#toc-wrapper"),s=$(".access"),a=$("#search-input");let l,n=0;const r=o.outerHeight();$(window).scroll(function(e){$("#topbar-title").is(":hidden")&&(l=!0)}),setInterval(function(){l&&(function(){var e=$(this).scrollTop();Math.abs(n-e)<=5||(e>n&&e>r?(o.removeClass("topbar-down").addClass("topbar-up"),0h1"),t=e.text().trim();let s=(0");$("body").append(o),o.val(e).select(),document.execCommand("copy"),o.remove(),alert("Link copied successfully!")}$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$("input[type=checkbox]").addClass("unloaded"),$("input[type=checkbox][checked]").before(''),$("input[type=checkbox]:not([checked])").before('')}),$(function(){const e=$("#sidebar-trigger"),o=$("#search-trigger"),t=$("#search-cancel"),a=$("#search-cleaner"),s=$("#main"),n=$("#topbar-title"),l=$("#search-wrapper"),r=$("#search-result-wrapper"),i=$("#search-results"),c=$("#search-input"),d=$("#search-hints"),u=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),h={on(){e.addClass("unloaded"),n.addClass("unloaded"),o.addClass("unloaded"),l.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),n.removeClass("unloaded"),o.removeClass("unloaded")}},f=function(){let e=!1;return{on(){e||(u.block(),r.removeClass("unloaded"),s.addClass("unloaded"),e=!0)},off(){e&&(i.empty(),d.hasClass("unloaded")&&d.removeClass("unloaded"),r.addClass("unloaded"),a.removeClass("visible"),s.removeClass("unloaded"),u.release(),c.val(""),e=!1)},isVisible(){return e}}}();function p(){return t.hasClass("loaded")}o.click(function(){h.on(),f.on(),c.focus()}),t.click(function(){h.off(),f.off()}),c.focus(function(){l.addClass("input-focus")}),c.focusout(function(){l.removeClass("input-focus")}),c.on("keyup",function(e){8===e.keyCode&&""===c.val()?p()?d.removeClass("unloaded"):f.off():""!==c.val()&&(f.on(),a.hasClass("visible")||a.addClass("visible"),p()&&d.addClass("unloaded"))}),a.on("click",function(){c.val(""),p()?(d.removeClass("unloaded"),i.empty()):f.off(),c.focus(),a.removeClass("visible")})}),$(function(){var e=function(){const e="sidebar-display";let o=!1;const t=$("body");return{toggle(){!1===o?t.attr(e,""):t.removeAttr(e),o=!o}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const o=$("#topbar-wrapper"),t=$("#toc-wrapper"),a=$(".access"),s=$("#search-input");let n,l=0;const r=o.outerHeight();$(window).scroll(function(e){$("#topbar-title").is(":hidden")&&(n=!0)}),setInterval(function(){n&&(function(){var e=$(this).scrollTop();Math.abs(l-e)<=5||(e>l&&e>r?(o.removeClass("topbar-down").addClass("topbar-up"),0h1"),t=e.text().trim();let a=(0");$("body").append(t),t.val(e).select(),document.execCommand("copy"),t.remove(),alert("Link copied successfully!")}$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$("input[type=checkbox]").addClass("unloaded"),$("input[type=checkbox][checked]").before(''),$("input[type=checkbox]:not([checked])").before('')}),$(function(){const e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#search-cancel"),s=$("#search-cleaner"),a=$("#main"),l=$("#topbar-title"),n=$("#search-wrapper"),r=$("#search-result-wrapper"),c=$("#search-results"),i=$("#search-input"),d=$("#search-hints"),u=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),l.addClass("unloaded"),t.addClass("unloaded"),n.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),n.removeClass("d-flex"),e.removeClass("unloaded"),l.removeClass("unloaded"),t.removeClass("unloaded")}},f=function(){let e=!1;return{on(){e||(u.block(),r.removeClass("unloaded"),a.addClass("unloaded"),e=!0)},off(){e&&(c.empty(),d.hasClass("unloaded")&&d.removeClass("unloaded"),r.addClass("unloaded"),s.removeClass("visible"),a.removeClass("unloaded"),u.release(),i.val(""),e=!1)},isVisible(){return e}}}();function h(){return o.hasClass("loaded")}t.click(function(){p.on(),f.on(),i.focus()}),o.click(function(){p.off(),f.off()}),i.focus(function(){n.addClass("input-focus")}),i.focusout(function(){n.removeClass("input-focus")}),i.on("keyup",function(e){8===e.keyCode&&""===i.val()?h()?d.removeClass("unloaded"):f.off():""!==i.val()&&(f.on(),s.hasClass("visible")||s.addClass("visible"),h()&&d.addClass("unloaded"))}),s.on("click",function(){i.val(""),h()?(d.removeClass("unloaded"),c.empty()):f.off(),i.focus(),s.removeClass("visible")})}),$(function(){var e=function(){const e="sidebar-display";let t=!1;const o=$("body");return{toggle(){!1===t?o.attr(e,""):o.removeAttr(e),t=!t}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const t=$("#topbar-wrapper"),o=$("#toc-wrapper"),s=$(".access"),a=$("#search-input");let l,n=0;const r=t.outerHeight();$(window).scroll(function(e){$("#topbar-title").is(":hidden")&&(l=!0)}),setInterval(function(){l&&(function(){var e=$(this).scrollTop();Math.abs(n-e)<=5||(e>n&&e>r?(t.removeClass("topbar-down").addClass("topbar-up"),0h1"),o=e.text().trim();let s=(0{const e=$(a);e.focus();var t="scroll-focus";if($(`[${t}=true]`).length&&$(`[${t}=true]`).attr(t,!1),$(":target").length&&$(":target").attr(t,!1),(s||o)&&e.attr(t,!0),e.is(":focus"))return!1;e.attr("tabindex","-1"),e.focus()})}}})}); \ No newline at end of file diff --git a/assets/js/dist/post.min.js b/assets/js/dist/post.min.js new file mode 100644 index 0000000..e8eafc2 --- /dev/null +++ b/assets/js/dist/post.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v3.3.2 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +function copyLink(e){e&&0!==e.length||(e=window.location.href);const t=$("");$("body").append(t),t.val(e).select(),document.execCommand("copy"),t.remove(),alert("Link copied successfully!")}$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$("input[type=checkbox]").addClass("unloaded"),$("input[type=checkbox][checked]").before(''),$("input[type=checkbox]:not([checked])").before('')}),$(function(){const e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#search-cancel"),a=$("#search-cleaner"),s=$("#main"),n=$("#topbar-title"),l=$("#search-wrapper"),r=$("#search-result-wrapper"),i=$("#search-results"),c=$("#search-input"),d=$("#search-hints"),u=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),h={on(){e.addClass("unloaded"),n.addClass("unloaded"),t.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),n.removeClass("unloaded"),t.removeClass("unloaded")}},f=function(){let e=!1;return{on(){e||(u.block(),r.removeClass("unloaded"),s.addClass("unloaded"),e=!0)},off(){e&&(i.empty(),d.hasClass("unloaded")&&d.removeClass("unloaded"),r.addClass("unloaded"),a.removeClass("visible"),s.removeClass("unloaded"),u.release(),c.val(""),e=!1)},isVisible(){return e}}}();function p(){return o.hasClass("loaded")}t.click(function(){h.on(),f.on(),c.focus()}),o.click(function(){h.off(),f.off()}),c.focus(function(){l.addClass("input-focus")}),c.focusout(function(){l.removeClass("input-focus")}),c.on("keyup",function(e){8===e.keyCode&&""===c.val()?p()?d.removeClass("unloaded"):f.off():""!==c.val()&&(f.on(),a.hasClass("visible")||a.addClass("visible"),p()&&d.addClass("unloaded"))}),a.on("click",function(){c.val(""),p()?(d.removeClass("unloaded"),i.empty()):f.off(),c.focus(),a.removeClass("visible")})}),$(function(){var e=function(){const e="sidebar-display";let t=!1;const o=$("body");return{toggle(){!1===t?o.attr(e,""):o.removeAttr(e),t=!t}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const t=$("#topbar-wrapper"),o=$("#toc-wrapper"),a=$(".access"),s=$("#search-input");let n,l=0;const r=t.outerHeight();$(window).scroll(function(e){$("#topbar-title").is(":hidden")&&(n=!0)}),setInterval(function(){n&&(function(){var e=$(this).scrollTop();Math.abs(l-e)<=5||(e>l&&e>r?(t.removeClass("topbar-down").addClass("topbar-up"),0h1"),o=e.text().trim();let a=(0{o.test(e)&&(e=e.substring(t.length),$(this).attr("lang",`${e}`))})})}),$(function(){$("a[href*='#']").not("[href='#']").not("[href='#0']").click(function(t){if(this.pathname.replace(/^\//,"")===location.pathname.replace(/^\//,"")&&location.hostname===this.hostname){const l=decodeURI(this.hash);let o=RegExp(/^#fnref:/).test(l),a=RegExp(/^#fn:/).test(l);var n=l.includes(":")?l.replace(/\:/,"\\:"):l;let s=$(n);if(s.length){t.preventDefault(),history.pushState&&history.pushState(null,null,l);n=$(this).offset().top;let e=s.offset().top;t=e{const e=$(s);e.focus();var t="scroll-focus";if($(`[${t}=true]`).length&&$(`[${t}=true]`).attr(t,!1),$(":target").length&&$(":target").attr(t,!1),(a||o)&&e.attr(t,!0),e.is(":focus"))return!1;e.attr("tabindex","-1"),e.focus()})}}})}); \ No newline at end of file diff --git a/assets/js/dist/pvreport.min.js b/assets/js/dist/pvreport.min.js new file mode 100644 index 0000000..f92bfc1 --- /dev/null +++ b/assets/js/dist/pvreport.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v3.3.2 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +const getInitStatus=function(){let e=!1;return()=>{var t=e;return e=e||!0,t}}(),PvOpts={getProxyEndpoint(){return $("meta[name=pv-proxy-endpoint]").attr("content")},getLocalData(){return $("meta[name=pv-cache-path]").attr("content")},hasLocalData(){var t=PvOpts.getLocalData();return void 0!==t&&!1!==t}},PvData=function(){const e="pv",a="pv_created_date",r="pv_source",n={ORIGIN:"origin",PROXY:"proxy"};function o(t){return localStorage.getItem(t)}function i(t,e){localStorage.setItem(t,e)}return{getData(){return JSON.parse(localStorage.getItem(e))},saveOriginCache(t){i(e,t),i(r,n.ORIGIN),i(a,(new Date).toJSON())},saveProxyCache(t){i(e,t),i(r,n.PROXY),i(a,(new Date).toJSON())},isFromOrigin(){return o(r)===n.ORIGIN},isFromProxy(){return o(r)===n.PROXY},isExpired(){if(PvData.isFromOrigin()){let t=new Date(o(a));return t.setDate(t.getDate()+1),Date.now()>=t.getTime()}if(PvData.isFromProxy()){let t=new Date(o(a));return t.setHours(t.getHours()+1),Date.now()>=t.getTime()}return!1},getAllPageviews(){return PvData.getData().totalsForAllResults["ga:pageviews"]},newerThan(t){return PvData.getAllPageviews()>t.totalsForAllResults["ga:pageviews"]},inspectKeys(){null!==localStorage.getItem(e)&&null!==localStorage.getItem(r)&&null!==localStorage.getItem(a)||localStorage.clear()}}}();function countUp(e,a,r){if(er&&countUp(r,n,a.attr("id"))):a.text((new Intl.NumberFormat).format(n))}function displayPageviews(t){if(void 0!==t){let e=getInitStatus();const a=t.rows;0<$("#post-list").length?$(".post-preview").each(function(){var t=$(this).find("a").attr("href");tacklePV(a,t,$(this).find(".pageviews"),e)}):0<$(".post").length&&(t=window.location.pathname,tacklePV(a,t,$("#pv"),e))}}function fetchProxyPageviews(){$.ajax({type:"GET",url:PvOpts.getProxyEndpoint(),dataType:"jsonp",jsonpCallback:"displayPageviews",success:(t,e,a)=>{PvData.saveProxyCache(JSON.stringify(t))},error:(t,e,a)=>{console.log("Failed to load pageviews from proxy server: "+a)}})}function fetchPageviews(t=!0,e=!1){t?fetch(PvOpts.getLocalData()).then(t=>t.json()).then(t=>{e&&PvData.newerThan(t)||(displayPageviews(t),PvData.saveOriginCache(JSON.stringify(t)))}).then(()=>fetchProxyPageviews()):fetchProxyPageviews()}$(function(){var t;$(".pageviews").length<=0||(PvData.inspectKeys(),(t=PvData.getData())?(displayPageviews(t),PvData.isExpired()?fetchPageviews(!0,PvData.isFromProxy()):PvData.isFromOrigin()&&fetchPageviews(!1)):fetchPageviews(PvOpts.hasLocalData()))}); \ No newline at end of file diff --git a/assets/js/highlight.js b/assets/js/highlight.js new file mode 100644 index 0000000..1f0c8ce --- /dev/null +++ b/assets/js/highlight.js @@ -0,0 +1,84 @@ +$(document).ready(() => { + + var all_gutters = document.getElementsByClassName("rouge-gutter"); + for (var j = 0; j < all_gutters.length; j++) { + all_gutters[j].preserved = all_gutters[j].children[0].innerHTML.split("\n"); + all_gutters[j].cur_length = all_gutters[j].preserved.length; + all_gutters[j].change_length = (s, l) => { + s.cur_length = s.cur_length + l; + s.children[0].innerHTML = s.preserved.slice(0, s.cur_length).join("\n"); + } + } + + var all_code = document.getElementsByClassName("rouge-code"); + var fold_amounts = []; + for (var j = 0; j < all_code.length; j++) { + var pre = all_code[j].children[0]; + var lines = pre.innerHTML.split("\n"); + var open = true; + var amount = 0; + for (var k = 0; k" + } + if (lines[k].includes("n")) { + lines[k] = "" + lines[k].replace("n", "") + "" + } + if (lines[k].includes("m")) { + lines[k] = "" + lines[k].replace("m", "") + "" + } + if (lines[k].includes("m")) { + lines[k] = "" + lines[k].replace("m", "") + "" + } + if (lines[k].includes("b")) { + lines[k] = "" + lines[k].replace("b", "") + "" + } + if (lines[k].includes("b")) { + lines[k] = "" + lines[k].replace("b", "") + "" + } + if (lines[k].includes("c")) { + if (open) { + lines[k] = lines[k].replace("c", ""); + amount = 0; + } else { + lines[k] = "" + lines[k].replace("c", ""); + pre.parentElement.parentElement.children[0].change_length(pre.parentElement.parentElement.children[0], -amount+1); + fold_amounts.push(amount-1); + } + open = !open; + } + if (lines[k].includes("c")) { + if (open) { + lines[k] = lines[k].replace("c", ""); + amount = 0; + } else { + lines[k-1] = lines[k-1] + "" + lines[k] = lines[k].replace("c", ""); + pre.parentElement.parentElement.children[0].change_length(pre.parentElement.parentElement.children[0], -amount+1); + fold_amounts.push(amount-1); + } + open = !open; + } + } + pre.innerHTML = lines.join("\n"); + } + + var all_folds = document.getElementsByClassName("code_fold"); + for (var j = 0; j < all_folds.length; j++) { + var but = all_folds[j].children[0]; + but.click_index = j; + but.onclick = (x) => { + var pre = x.target.parentElement.parentElement.parentElement.parentElement.children[0]; + if (x.target.parentElement.classList.contains("code_fold_closed")) { + pre.change_length(pre, fold_amounts[x.target.click_index]); + x.target.parentElement.classList.remove("code_fold_closed"); + x.target.parentElement.classList.add("code_fold_opened"); + } else { + pre.change_length(pre, -fold_amounts[x.target.click_index]); + x.target.parentElement.classList.add("code_fold_closed"); + x.target.parentElement.classList.remove("code_fold_opened"); + } + } + } +}); diff --git a/assets/js/hints.js b/assets/js/hints.js new file mode 100644 index 0000000..cbac22d --- /dev/null +++ b/assets/js/hints.js @@ -0,0 +1,14 @@ +$(document).ready(() => { + + hints = document.getElementsByClassName("button_unlock"); + for (var i = 0; i < hints.length; i++) { + hints[i].onclick = function() { + var cur = this; + while (!cur.classList.contains("unlock")) { + cur = cur.parentNode; + } + cur.classList.toggle("unlock"); + }; + } + +}); diff --git a/assets/js/lib/jquery.disqusloader.min.js b/assets/js/lib/jquery.disqusloader.min.js new file mode 100644 index 0000000..b2cb0be --- /dev/null +++ b/assets/js/lib/jquery.disqusloader.min.js @@ -0,0 +1,8 @@ +/*! + disqusLoader.js v1.0 + A JavaScript plugin for lazy-loading Disqus comments widget. + - + By Osvaldas Valutis, www.osvaldas.info + Available for use under the MIT License +*/ +(function(e,g,h,d){var a=e(g),k=function(o,n){var q,p;return function(){var t=this,s=arguments,r=+new Date;q&&ra.height()*j||n-b.offset().top-b.outerHeight()-(a.height()*j)>0){return true}e("#disqus_thread").removeAttr("id");b.attr("id","disqus_thread").data("disqusLoaderStatus","loaded");if(f=="loaded"){DISQUS.reset({reload:true,config:i})}else{g.disqus_config=i;if(f=="unloaded"){f="loading";e.ajax({url:c,async:true,cache:true,dataType:"script",success:function(){f="loaded"}})}}};a.on("scroll resize",k(m,l));e.disqusLoader=function(o,n){n=e.extend({},{laziness:1,throttle:250,scriptUrl:false,disqusConfig:false},n);j=n.laziness+1;m=n.throttle;i=n.disqusConfig;c=c===false?n.scriptUrl:c;b=(typeof o=="string"?e(o):o).eq(0);b.data("disqusLoaderStatus","unloaded");l()}})(jQuery,window,document); \ No newline at end of file diff --git a/categories/contests/index.html b/categories/contests/index.html new file mode 100644 index 0000000..739650c --- /dev/null +++ b/categories/contests/index.html @@ -0,0 +1 @@ + Contests | Monash Code Binder
Home Categories Contests
Category
Cancel
diff --git a/categories/data-structures/index.html b/categories/data-structures/index.html new file mode 100644 index 0000000..8544254 --- /dev/null +++ b/categories/data-structures/index.html @@ -0,0 +1 @@ + Data Structures | Monash Code Binder
Home Categories Data Structures
Category
Cancel
diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..ecec670 --- /dev/null +++ b/categories/index.html @@ -0,0 +1 @@ + Categories | Monash Code Binder
Home Categories
Categories
Cancel
diff --git a/categories/math/index.html b/categories/math/index.html new file mode 100644 index 0000000..5b1c67f --- /dev/null +++ b/categories/math/index.html @@ -0,0 +1 @@ + Math | Monash Code Binder
Home Categories Math
Category
Cancel
diff --git a/categories/trees/index.html b/categories/trees/index.html new file mode 100644 index 0000000..7b824e4 --- /dev/null +++ b/categories/trees/index.html @@ -0,0 +1 @@ + Trees | Monash Code Binder
Home Categories Trees
Category
Cancel
diff --git a/feed.xml b/feed.xml new file mode 100644 index 0000000..6bad25a --- /dev/null +++ b/feed.xml @@ -0,0 +1 @@ + https://monashaps.github.io//Monash Code BinderA collection of algorithms, explanations and training problems 2023-12-28T23:03:47+11:00 Monash Programming Team https://monashaps.github.io// Jekyll © 2023 Monash Programming Team /assets/img/favicons/favicon.ico /assets/img/favicons/favicon-96x96.png DataStructureLess Competition 2023 Editorial2023-12-28T18:00:00+11:00 2023-12-28T23:02:40+11:00 https://monashaps.github.io//posts/dsless-editorial/ Monash Programming Team Since the intention of the DataStructureLess Competition was to showcase some interesting/unique solve techniques, I thought it would be good to provide some editorial for all of the problems so everyone can see some of the cool stuff on offer. Each problem has been given a few hints, so you can hopefully have a stab at the solution even if you got stuck in contest, but a solution is also prov... Union Find / DSU2021-12-15T12:00:00+11:00 2021-12-27T13:53:13+11:00 https://monashaps.github.io//posts/uf/ Monash Programming Team Where is this useful? In many problems, translating into a graph structure can prove helpful, as we can describe our problem in very abstract terms. Once you’ve translated into this graph structure, often you might want to know whether two vertices are connected via a path, and if this is not the case, what two separate components they come from. Union Find allows us to not only answer this q... Challenge Problems - 2021 Sem 2, Contest 12021-08-23T11:00:00+10:00 2021-08-23T11:00:00+10:00 https://monashaps.github.io//posts/problems-21-s2-c1/ Monash Programming Team Sports Loans Statement Andrew is head of the sports club, and manages the inventory. Part of Andrew’s job is loaning footballs to people, and collecting those footballs once they have been used. At the start of the day, Andrew has \(r\) footballs in stock, and knows that \(p+q\) people will approach him over the course of the day. \(p\) people will request a football, while \(q\) people will... Least Common Ancestor (LCA)2021-04-20T22:00:00+10:00 2021-12-13T11:01:10+11:00 https://monashaps.github.io//posts/lca/ Monash Programming Team Where is this useful? The Least Common Ancestor (LCA) data structure is useful wherever you have a directed graph where every vertex has out-degree \(\leq 1\). In more common terms, each vertex has a unique determined ‘parent’, or it is a root node, with no parent. The most common (and almost always only) example being a rooted tree. On these particular graphs, the LCA gives us a fast way to ... Primes and Factorization Techniques2021-04-05T11:00:00+10:00 2021-04-05T21:02:26+10:00 https://monashaps.github.io//posts/factorization/ Monash Programming Team Why? Many number theoretic problems in competitive programming require analysing the factors or prime factors of a number. Here I’ll list a few techniques for finding these factors, and some techniques / properties involving the factors / prime factors of a number. Preliminaries First off, lets define our basic terms, then we can get into the interesting stuff. Definitions For a positive i... diff --git a/index.html b/index.html new file mode 100644 index 0000000..ef04138 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ + Monash Code Binder
Posts
Monash Code Binder
Cancel

DataStructureLess Competition 2023 Editorial

Since the intention of the DataStructureLess Competition was to showcase some interesting/unique solve techniques, I thought it would be good to provide some editorial for all of the problems so ev...

Union Find / DSU

Where is this useful? In many problems, translating into a graph structure can prove helpful, as we can describe our problem in very abstract terms. Once you’ve translated into this graph structu...

Challenge Problems - 2021 Sem 2, Contest 1

Sports Loans Statement Andrew is head of the sports club, and manages the inventory. Part of Andrew’s job is loaning footballs to people, and collecting those footballs once they have been used. ...

Least Common Ancestor (LCA)

Where is this useful? The Least Common Ancestor (LCA) data structure is useful wherever you have a directed graph where every vertex has out-degree \(\leq 1\). In more common terms, each vertex ha...

Primes and Factorization Techniques

Why? Many number theoretic problems in competitive programming require analysing the factors or prime factors of a number. Here I’ll list a few techniques for finding these factors, and some techn...

Modular Arithmetic

What is it? Modular Arithmetic encompasses all sorts of theorems and optimizations surrounding the % operator in C and Python. As you’ll see in the related problems, modulo arithmetic is often ti...

Dynamic Programming

Why? Dynamic Programming (DP) is one of the most powerful tools you’ll come across in competitive programming. It normally turns up in about a third of all problems in a contest, in some form or a...

diff --git a/norobots/index.html b/norobots/index.html new file mode 100644 index 0000000..c31a99c --- /dev/null +++ b/norobots/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/posts/dp/index.html b/posts/dp/index.html new file mode 100644 index 0000000..3ec59c2 --- /dev/null +++ b/posts/dp/index.html @@ -0,0 +1,153 @@ + Dynamic Programming | Monash Code Binder
Posts Dynamic Programming
Post
Cancel

Dynamic Programming

Why?

Dynamic Programming (DP) is one of the most powerful tools you’ll come across in competitive programming. It normally turns up in about a third of all problems in a contest, in some form or another.

What is it?

Dynamic Programming is a general tactic centred around storing previous calculations so that you don’t need to recompute the outcome of certain functions. While this might seem like common sense, these precomputed calculations can hide themselves quite well.

Example

One of the most popular applications of DP in contests is in solving recurrence relations in a short amount of time. Consider the following problem, Cut Ribbon:

Polycarpus has a ribbon, its length is n. He wants to cut the ribbon in a way that fulfils the following two conditions:

  • After the cutting each ribbon piece should have length a, b or c.
  • After the cutting the number of ribbon pieces should be maximum.

Help Polycarpus and find the number of ribbon pieces after the required cutting.

Input

The first line contains four space-separated integers n, a, b and c (1 ≤ n, a, b, c ≤ 4000) — the length of the original ribbon and the acceptable lengths of the ribbon pieces after the cutting, correspondingly. The numbers a, b and c can coincide.

Output

Print a single number — the maximum possible number of ribbon pieces. It is guaranteed that at least one correct ribbon cutting exists.

Solution

Let’s try to create a function max_cuts(x), which tells us the maximum number of cuts for a ribbon of size x. We want to compute max_cuts(n).

Since every possible cutting of the ribbon must start with a cut of size a, b, or c, max_cuts(x) must be either max_cuts(x-a)+1, max_cuts(x-b)+1 or max_cuts(x-c)+1 (Assuming base case max_cuts(0) == 0).

Therefore, we can define max_cuts recursively in the following way:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
def max_cuts(x):
+    if x == 0:
+        return 0
+    best = 0
+    for cut in (a, b, c):
+        if x - cut >= 0:
+            best = max(best, max_cuts(x-cut) + 1)
+    if best == 0:
+        # Not possible to cut this length.
+        best = -100000
+    return best
+
+# We then just call
+max_cuts(n)
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
int max_cuts(int x) {
+    if (x == 0) return 0;
+    int best = 0;
+    if (x - a >= 0) best = max(best, max_cuts(x - a) + 1);
+    if (x - b >= 0) best = max(best, max_cuts(x - b) + 1);
+    if (x - c >= 0) best = max(best, max_cuts(x - c) + 1);
+    // Not possible to cut this length.
+    if (best == 0) best = -100000;
+    return best;
+}
+

However, if you submit this as is, you will almost certainly get TLE, despite n <= 4000. Why is this?

Lets inspect the call tree for max_cuts with n=5, and a, b, c = 1, 2, 3:

As you can see, despite only taking 6 different values, max_cuts is being called a bunch of times, which is unnecessary! Instead, we can save each value with DP!

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
# DP[x] = max_cut(x) if computed, or -1.n
+DP = [-1] * (n+1)n
+
+def max_cuts(x):
+    if DP[x] != -1:n
+        # If already computed, just return the value!n
+        return DP[x]n
+    if x == 0:
+        DP[x] = 0m
+        return DP[x]m
+    best = 0
+    for cut in (a, b, c):
+        if x - cut >= 0:
+            best = max(best, max_cuts(x-cut) + 1)
+    if best == 0:
+        # Not possible
+        DP[x] = -100000m
+        return DP[x]m
+    # Set the DP value, so that future calls to max_cuts(x) just use DP[x].n
+    DP[x] = bestn
+    return DP[x]m
+
+max_cuts(n)
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+
// DP[x] = max_cut(x) if computed, or -1.n
+int DP[4002];n
+
+int max_cuts(int x) {
+    // If already computed, just return the value!n
+    if (DP[x] != -1) return DP[x];n
+    if (x == 0) {
+        DP[x] = 0;m
+        return DP[x];m
+    }
+    int best = 0;
+    if (x - a >= 0) best = max(best, max_cuts(x-a) + 1);
+    if (x - b >= 0) best = max(best, max_cuts(x-b) + 1);
+    if (x - c >= 0) best = max(best, max_cuts(x-c) + 1);
+    if (best == 0) {
+        // Not possible.
+        DP[x] = -100000;m
+        return DP[x];m
+    }
+    // Set the DP value, so that future calls to max_cuts(x) just use DP[x].n
+    DP[x] = best;n
+    return DP[x];m
+}
+
+int main() {
+    // Initiliase DP
+    for (int i=0; i<4002; i++) DP[i] = -1;
+    cout << max_cuts(n) << endl;
+}
+

This won’t TLE, because we only need to compute 4001 values, max_cuts(x) for any x from 0 to n.

This post is licensed under GNU GPL V3 by the author.
diff --git a/posts/dsless-editorial/index.html b/posts/dsless-editorial/index.html new file mode 100644 index 0000000..6a5389d --- /dev/null +++ b/posts/dsless-editorial/index.html @@ -0,0 +1,2653 @@ + DataStructureLess Competition 2023 Editorial | Monash Code Binder
Posts DataStructureLess Competition 2023 Editorial
Post
Cancel

DataStructureLess Competition 2023 Editorial

Since the intention of the DataStructureLess Competition was to showcase some interesting/unique solve techniques, I thought it would be good to provide some editorial for all of the problems so everyone can see some of the cool stuff on offer.

Each problem has been given a few hints, so you can hopefully have a stab at the solution even if you got stuck in contest, but a solution is also provided.

Binary 1

Hint 1

Simulating won’t be enough, because of the size of $i$. We need to somehow skip most of the previous values.

Hint 2

Note that the lengths of the binary numbers increase as we move along the sequence, in fact there are $2^k$ binary numbers of length $k+1$

Solution

Assuming you’ve read the previous two hints, we want to skip ‘blocks’ of binary numbers of equal length. Since these blocks at least double in size each time we can get rid of an exponential amount of numbers before our index. We can continue subtracting these larger and larger blocks until our index would be exceeded by the next block: a jump of size $(k+1) * 2^k$, which tells us that the value we are trying to find is within a binary number of length $k+1$.

Now we know that after dealing with the first $p$ digits (Which contains all binary strings with less than $k+1$ length), we are left to find the $i-p^{th}$ value in the sequence of binary strings of length $k+1$. But since all binary strings are the same length now, we know we’re actually looking at the $\frac{i-p}{k+1}^{th}$ binary string in that sequence! From here we can just do some indexing to get what we need.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
index = int(input())
+
+k = 0
+
+# While our index is not in the next block of binary strings of length k+1
+while index > (1 << k) * (k+1):
+    # Subtract our index to "offset" removing those binary strings
+    index -= (1 << k) * (k+1)
+    k += 1
+
+bit_length = k+1
+
+# 0 index, rather than 1-index.
+index -= 1
+# The jth binary string of length k+1 is 2^k + j (j is 0-indexed)
+skip_num = index // bit_length
+actual_num = (1 << k) + skip_num
+
+# Now the remaining index is simply the index of our singular binary number
+index = index % bit_length
+
+print(bin(actual_num)[2:][index])
+

Complexity $\mathcal{O}(\log_2(n))$

Binary 2

Hint 1

If you’ve solved Binary 1, we need to make a similar revelation about jumps.

Hint 2

Notice that in blocks of binary strings of equal size, the first bit is always 1, and every other bit is equal parts 0 and 1.

Solution

As the hints note, since we cycle through every binary number in a block, the numbers 0 and 1 appear the same amount, except for the first bit of every number, which is always 1.

Therefore for a string of $2^k$ binary numbers of size $k+1$, they contain $k*2^{k-1} + 2^k$ 1s.

Once we’ve dealt with everything except our block, rather than iterating through the final block, we can make use of this fact for “subblocks”.

For example, if our final number starts with “11”, it means that all binary strings of length $k+1$ starting with “10” are also included, so the last $k-1$ bits in all these numbers have an equal amount of 0s and 1s. If instead our final number starts with “10”, then we can simply recurse down. This is a bit hard to express in code but hopefully the logic above is clear.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+
import sys
+
+index = int(input())
+
+total_ones = 0
+k = 0
+bit_length = 1
+
+while index > (1 << k) * (k+1):
+    index -= (1 << k) * (k+1)
+    if k == 0:
+        total_ones += 1
+    else:
+        # Same formula as $k*2^{k-1} + 2^k$
+        total_ones += (1 << (k - 1)) * (k + 2)
+    bit_length += 1
+
+# We've counted all 1s in the prior blocks.
+
+# 0 index.
+index -= 1
+# Our number has this bit_length.
+skip_num = index // bit_length
+actual_num = (1 << (bit_length-1)) + skip_num
+
+def rec(prev_ones, min_val, max_val, power):
+    # Recursive function to count all 1s in our current block.
+    # prev_ones: 1s to the left of our current bit (IE, if we've got to our binary number starting with `1101`, then there are 3 previous 1s, which will always be 1s for future binary strings)
+    # min_val: The minimum value of the search block
+    # max_val: The maximum value of the search block
+    # ^ These two will squish together by powers of 2
+    # power: The power of 2 we are searching for next (decreases by 1 each time)
+    global total_ones
+    if power < 0:
+        return
+    print(f"{min_val} {max_val} jump {1 << power} ones {prev_ones}", file=sys.stderr)
+    # min_val is always a power of 2
+    # max_val is either a power of 2 or smaller (Since it starts as the actual number we are looking for).
+    if min_val + (1 << power) <= max_val:
+        # Our number has a `1` in the nth bit
+        # We can skip to the right half and count all the 1s in the left!
+        # First, add all the static 1s.
+        total_ones += (1 << power) * prev_ones
+        print(f"{(1 << power) * prev_ones} ones added from previous indicies", file=sys.stderr)
+        if power > 0:
+            # And also add the ones which occur with 50% frequency.
+            total_ones += (1 << (power-1)) * power
+            print(f"{(1 << (power-1)) * power} extra ones in the left half added", file=sys.stderr)
+            # recurse
+            rec(prev_ones+1, min_val + (1 << power), max_val, power-1)
+    else:
+        # Our number has a `0` in the nth bit
+        # We are in the left half
+        if power > 0:
+            rec(prev_ones, min_val, max_val, power-1)
+
+print(f"Num lives in {actual_num}", file=sys.stderr)
+rec(1, 1 << (bit_length-1), actual_num, bit_length-2)
+
+index = index % bit_length
+# rec doesn't count the final number.
+total_ones += bin(actual_num)[2:][:index+1].count("1")
+
+print(total_ones)
+

Complexity $\mathcal{O}(\log_2(n))$

Binary 3

Hint 1

Note that for even jump sizes, the answer is the same if we just divide the jump size by 2. So you can assume the jump size is odd.

Hint 2

While we don’t quite have the same nice rule about equal numbers of 1s and 0s, there is still some structure in our bits. For example, not (assuming odd jump size) that the least significant bit always toggles between 0 and 1. What happens to the second bit, the third?

Solution

The revelation here is that if we look at the first $2^k$ numbers in the sequence, the $k$ least significant bits actually do have an equal number of 0s and 1s! There are a few nice proofs of this, and I’ll leave it as a task for the reader to attempt (Hint: Note that if the jump size is odd, the jump size and $2^k$ are coprime).

So, we can continue some similar logic here to get rid of the first $k$ bits to deal with (and since we are dealing with a power of 2 as input, we don’t have to worry about our ‘current’ block).

Now all we need to do is worry about the extra bits we missed. Note that the jump size determines how many extra bits there are. In general, we should have $\log_2(j)$ extra bits to deal with. But this means that there’s at most $\approx j$ unique values for these extra bits, so we can simply find all of these values and add them up separately, by recursing in blocks of size $2^p$.

While you can solve this recursively, using the fact that the number of values divisible by $j$ in the range $(a, b]$ is $\lfloor \frac{b}{j} \rfloor - \lfloor \frac{a}{j} \rfloor$, as team de noted, you can also just use this formula between $[2^k\times a, 2^k\times (a+1))$ for every $a$ from 0 to $j$.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+
import sys
+from math import log2, floor, ceil
+
+repeats, jump = list(map(int, input().split()))
+
+# Make jump odd.
+while jump % 2 == 0:
+    jump //= 2
+
+total_ones = 0
+# First, determine how many of the first k bits can be handled separately.
+handled_bit_length = floor(log2(repeats))
+
+# Handle the first handled_bit_length bits.
+total_ones += (1 << (handled_bit_length - 1)) * handled_bit_length if handled_bit_length >= 1 else 0
+
+print(f"Handled {total_ones} ones in the known block.", file=sys.stderr)
+
+# Now we need to count the occurence of all bits after this in the sequence.
+def rec(minval, maxval, cur_bit):
+    # rec checks for all 1s in cur_bit between minval and maxval.
+    global total_ones
+
+    # If cur_bit gets too small, we'll start double counting the bits we handled separately.
+    if cur_bit <= handled_bit_length - 1:
+        return
+    midway = minval + (1 << cur_bit)
+    if midway <= maxval:
+        # We have some space in the '1' of this cur_bit.
+        # Count how many numbers are within that are divisible by `jump`.
+        print(f"{maxval // jump - (midway-1) // jump} values in [{midway}, {maxval}], and all of these have a 1 in the {cur_bit}th bit.", file=sys.stderr)
+        total_ones += maxval // jump - (midway-1) // jump
+
+        # Recurse on the right branch
+        rec(midway, maxval, cur_bit-1)
+    # Recurse on the left branch
+    rec(minval, min(midway-1, maxval), cur_bit-1)
+
+rec(0, repeats*jump, ceil(log2(repeats * jump)))
+
+print(total_ones)
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+
i,j = map(int, input().split())
+
+while (j%2 == 0):
+    j //= 2
+
+
+k = 0
+iCopy = i
+while iCopy > 1:
+    k += 1
+    iCopy//=2
+
+s = [0]
+for x in range(1,j+1):
+    s.append(x%2 + s[x//2])
+
+# s[x] = # 1 bits in the binary representation of x.
+
+tot = 0
+for x in range(j + 1):
+    # count the occurences of s[x]*2^k up until s[x+1]*2^k.
+    tot += (min((i*(x+1)-1), i*j)//j - (i*x-1)//j)*s[x]
+
+# Add the number of 1s in the least k significant bits
+tot += k*i//2
+
+print(tot)
+

Complexity $\mathcal{O}(\log_2(i) + j)$

Coins 1

This is a classic problem

Hint 1

The bounds imply a logarithmic solution. What’s the base of the logarithm?

Hint 2

Something akin to binary search would be good, although the binary search is optimal for a usual search because there are 2 equally possible outcomes for a query (value is left than or greater than the tester, equality means we stop immediately)

How many possible outcomes can the seesaw have? How can we use this to design a faster search?

Solution

The solution is to recognise that we want our query to break the solution space into three parts, depending on the seesaw result. We can do this by weighing one third of the remaining coins against another third. Then:

  • If the left side is heavier, we need only recurse on that third of the coins
  • If the right side is heavier, we need only recurse on that third of the coins
  • If the left and right side are equal, then the fake coin must not have been weighed, so we recurse on the remaining third of the coins.
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+
def solve(coins):
+    if len(coins) == 1:
+        return coins[0]
+    if len(coins) == 2:
+        print(f"TEST {coins[0]} | {coins[1]}")
+        res = input()
+        if res == "LEFT":
+            return coins[0]
+        elif res == "RIGHT":
+            return coins[1]
+        else:
+            raise ValueError()
+    amount = len(coins) // 3
+    coin_left = coins[:amount]
+    coin_right = coins[amount:2*amount]
+    coin_extra = coins[2*amount:]
+    print(f"TEST {' '.join(map(str, coin_left))} | {' '.join(map(str, coin_right))}")
+    res = input()
+    if res == "LEFT":
+        return solve(coin_left)
+    elif res == "RIGHT":
+        return solve(coin_right)
+    elif res == "EQUAL":
+        return solve(coin_extra)
+
+n = int(input())
+
+coins = list(range(1, n+1))
+
+print("ANS", solve(coins))
+

Complexity: $\mathcal{O}(\log_3(n))$

Coins 2

A similar idea for a problem, with some added intricacy - How do I recurse quickly to resolve where the 2 coins are?

Hint 1

Obviously since the setup is the same if we can place the 2 coins in separate piles, then we can simply apply the previous solution to solve within time.

So our solution needs to either:

  1. Recurse into a smaller problem with 2 fake coins
  2. Separate into two separate problems with a single fake coin each

Hint 2

We can’t quite immediately split into 3 evenly distributed problems because each seesaw option can feasibly be caused by two different configurations (For example, the left pile being bigger could be 2 in left, 0 elsewhere, or 1 in left and 1 unweighed).

Can we either:

  1. Change what we’re weighing so that this isn’t the case? or
  2. Provide additional weighing steps to disambiguate.

Solution

Following on from Hint 2, let’s follow these two options to two different solutions.

Option 1: Change what we query

Note that the fact that we have a third of the coins unweighed is the main cause of ambiguity. If there was a way to limit the size of the unweighed portion then our problems would mostly go away. So rather than splitting into thirds, lets do the original naive thing for coins 1, splitting in half, and only at most 1 coin will miss out from weighing. Then:

  • If the seesaw goes LEFT, then all fake coins are in the left pile (or the additional unweighed)
  • If the seesaw goes RIGHT, then all fake coins are in the right pile (or the additional unweighed)
  • If the seesaw is EQUAL, then the additional coin cannot be fake. There must be a fake coin in each of the two weighed piles

This solution will have the maximum of $\log_2(n)$ and $2\log_3(n)$ queries ($2\log_3(n)$).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+
def solve_1(coins):
+    # log_3(n)
+    if len(coins) == 1:
+        return coins[0]
+    if len(coins) == 2:
+        print(f"TEST {coins[0]} | {coins[1]}")
+        res = input()
+        if res == "LEFT":
+            return coins[0]
+        elif res == "RIGHT":
+            return coins[1]
+        else:
+            raise ValueError()
+    amount = len(coins) // 3
+    coin_left = coins[:amount]
+    coin_right = coins[amount:2*amount]
+    coin_extra = coins[2*amount:]
+    print(f"TEST {' '.join(map(str, coin_left))} | {' '.join(map(str, coin_right))}")
+    res = input()
+    if res == "LEFT":
+        return solve_1(coin_left)
+    elif res == "RIGHT":
+        return solve_1(coin_right)
+    elif res == "EQUAL":
+        return solve_1(coin_extra)
+
+def solve_2(coins):
+    # 2*log_3(n)
+    if len(coins) == 2:
+        return coins
+    amount = len(coins) // 2
+    coin_left = coins[:amount]
+    coin_right = coins[amount:2*amount]
+    coin_extra = coins[2*amount:]
+    print(f"TEST {' '.join(map(str, coin_left))} | {' '.join(map(str, coin_right))}")
+    res = input()
+    if res == "LEFT":
+        return solve_2(coin_left + coin_extra)
+    elif res == "RIGHT":
+        return solve_2(coin_right + coin_extra)
+    elif res == "EQUAL":
+        return solve_1(coin_left), solve_1(coin_right)
+
+
+n = int(input())
+
+coins = list(range(1, n+1))
+
+print("ANS", *solve_2(coins))
+
+

Option 2: Add a clarifying additional query.

This solution is more complicated, where we instead add an additional query to resolve the initial result.

Let’s call the state x-y-z if there are x fake coins in the left pile, y in the right, and z in the remaining unweighed coins

  • If the original query is LEFT, then this is either 2-0-0 or 1-0-1.
    • We can add an additional query comparing one half of extra to the other half of extra
    • If the second query is left or right, it is 1-0-1 and we can recurse
    • If the second query is equal, then it is 2-0-0 (or the extra coins were odd and the remaining unweighed is fake), and we can recurse
  • Same rule applies for RIGHT, either 0-2-0 or 0-1-1.
  • If the original query is EQUAL, then this is either 1-1-0 or 0-0-2.
    • We can resolve this by weighing all of the unweighed coins against a combination of left and right coins.
    • If the second query says the LEFT, then the unweighed coins are heavier and we recurse on the unweighed coins
    • If the second query says the RIGHT, then the left/right pile coins have a fake coin each
    • If the second query says EQUAL, then the left/right pile coins we haven’t chosen are the ones that must have a fake coin each
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+
def solve_1(coins):
+    # log_3(n)
+    if len(coins) == 1:
+        return coins[0]
+    if len(coins) == 2:
+        print(f"TEST {coins[0]} | {coins[1]}")
+        res = input()
+        if res == "LEFT":
+            return coins[0]
+        elif res == "RIGHT":
+            return coins[1]
+        else:
+            raise ValueError()
+    amount = len(coins) // 3
+    coin_left = coins[:amount]
+    coin_right = coins[amount:2*amount]
+    coin_extra = coins[2*amount:]
+    print(f"TEST {' '.join(map(str, coin_left))} | {' '.join(map(str, coin_right))}")
+    res = input()
+    if res == "LEFT":
+        return solve_1(coin_left)
+    elif res == "RIGHT":
+        return solve_1(coin_right)
+    elif res == "EQUAL":
+        return solve_1(coin_extra)
+
+def solve_2(coins):
+    # 2*log_3(n)
+    if len(coins) == 2:
+        return coins
+    amount = len(coins) // 3
+    if 2 * amount < len(coins) - 2*amount:
+        # This essentially just deals with 5.
+        amount += 1
+    coin_left = coins[:amount]
+    coin_right = coins[amount:2*amount]
+    coin_extra = coins[2*amount:]
+    print(f"TEST {' '.join(map(str, coin_left))} | {' '.join(map(str, coin_right))}")
+    res = input()
+    if res == "LEFT":
+        # Either 2-0-0
+        # or 1-0-1.
+        # Check by comparing half of extra against itself.
+        # Some base cases for the second test:
+        if len(coin_left) == 1:
+            return solve_1(coin_left), solve_1(coin_extra)
+        if len(coin_extra) == 1:
+            print(f"TEST {coin_left[0]} | {coin_extra[0]}")
+            res2 = input()
+            if res2 == "LEFT":
+                return solve_2(coin_left)
+            elif res2 == "RIGHT":
+                return solve_1(coin_left[1:]), solve_1(coin_extra)
+            elif res2 == "EQUAL":
+                # Since coin_left == 2
+                return solve_1(coin_left[:1]), solve_1(coin_extra)
+            return None
+        # Now the meat and potatoes
+        extra_amount = len(coin_extra) // 2
+        extra_left = coin_extra[:extra_amount]
+        extra_right = coin_extra[extra_amount:2*extra_amount]
+        extra_extra = coin_extra[2*extra_amount:] # read all about it
+        print(f"TEST {' '.join(map(str, extra_left))} | {' '.join(map(str, extra_right))}")
+        res2 = input()
+        if res2 == "LEFT":
+            # 1-0-1-0
+            return solve_1(coin_left), solve_1(extra_left)
+        elif res2 == "RIGHT":
+            # 1-0-0-1
+            return solve_1(coin_left), solve_1(extra_right)
+        elif res2 == "EQUAL":
+            # 2-0-0-0 (plus extra_extra)
+            return solve_2(coin_left + extra_extra)
+    elif res == "RIGHT":
+        # Either 0-2-0
+        # or 0-1-1.
+        # Check by comparing half of extra against itself.
+        # Some base cases for the second test:
+        if len(coin_right) == 1:
+            return solve_1(coin_right), solve_1(coin_extra)
+        if len(coin_extra) == 1:
+            print(f"TEST {coin_right[0]} | {coin_extra[0]}")
+            res2 = input()
+            if res2 == "LEFT":
+                return solve_2(coin_right)
+            elif res2 == "RIGHT":
+                return solve_1(coin_right[1:]), solve_1(coin_extra)
+            elif res2 == "EQUAL":
+                # Since coin_right == 2
+                return solve_1(coin_right[:1]), solve_1(coin_extra)
+            return None
+        extra_amount = len(coin_extra) // 2
+        extra_left = coin_extra[:extra_amount]
+        extra_right = coin_extra[extra_amount:2*extra_amount]
+        extra_extra = coin_extra[2*extra_amount:] # read all about it
+        print(f"TEST {' '.join(map(str, extra_left))} | {' '.join(map(str, extra_right))}")
+        res2 = input()
+        if res2 == "LEFT":
+            # 0-1-1-0
+            return solve_1(coin_right), solve_1(extra_left)
+        elif res2 == "RIGHT":
+            # 0-1-0-1
+            return solve_1(coin_right), solve_1(extra_right)
+        elif res2 == "EQUAL":
+            # 0-2-0-0 (plus extra_extra)
+            return solve_2(coin_right + extra_extra)
+    elif res == "EQUAL":
+        # Either 1-1-0 or 0-0-2
+        # Resolve by weighing all of extra against some subset of left+right
+        not_extra = (coin_left + coin_right)[:len(coin_extra)]
+        print(f"TEST {' '.join(map(str, coin_extra))} | {' '.join(map(str, not_extra))}")
+        res2 = input()
+        if res2 == "LEFT":
+            # 0-0-2
+            return solve_2(coin_extra)
+        elif res2 == "RIGHT":
+            return solve_1(coin_left), solve_1(coin_right)
+        elif res2 == "EQUAL":
+            # only 1-1-0 is possible, when n=5,
+            return solve_1(coin_left), solve_1(coin_right)
+
+
+n = int(input())
+
+coins = list(range(1, n+1))
+
+print("ANS", *solve_2(coins))
+

Complexity: $\mathcal{O}(2\log_3(n))$

Coins 3

The new style seesaw requires us to completely ignore past solutions and find 3 fake coins

Hint 1

The single coin version of the problem can be solved in $\mathcal{O}(\log_4(n))$ guesses.

The double coin version of the problem can be solved in $\mathcal{O}(\log_2(n))$ guesses.

Hint 2

If you’ve solved the previous two problems, this should really just be applying the same mantra - how can I make 1/2 guesses to completely disambiguate which pile of coins the fake coins lie in.

Solution

Let’s start off by coding in solve1 and solve2, as there isn’t much interesting to this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+
def guess(c1, c2, c3):
+    print("TEST", " ".join(map(str, c1)), "|", " ".join(map(str, c2)), "|", " ".join(map(str, c3)))
+    return [
+        list(map(int, section.strip().split()))
+        for section in input().split(">")
+    ]
+
+def solve_1(coins):
+    if len(coins) == 1:
+        return coins[0]
+    elif len(coins) == 2:
+        res = guess(coins[:1], coins[1:], [])
+        assert len(res) == 3
+        if res[0][0] == 1:
+            return coins[0]
+        else:
+            return coins[1]
+    amount = (len(coins) + 1) // 4
+    coin1 = coins[:amount]
+    coin2 = coins[amount:2*amount]
+    coin3 = coins[2*amount:3*amount]
+    coin4 = coins[3*amount:]
+    res = guess(coin1, coin2, coin3)
+    assert len(res) != 3
+    if len(res) == 2:
+        # The heavier one is alone.
+        if res[0][0] == 1:
+            return solve_1(coin1)
+        if res[0][0] == 2:
+            return solve_1(coin2)
+        if res[0][0] == 3:
+            return solve_1(coin3)
+    else:
+        # All are equal, the remainder is the culprit.
+        return solve_1(coin4)
+
+def solve_2(coins):
+    if len(coins) == 2:
+        return coins[0], coins[1]
+    elif len(coins) == 3:
+        res = guess([coins[0]], [coins[1]], [coins[2]])
+        cur = []
+        if 1 in res[0]:
+            cur.append(coins[0])
+        if 2 in res[0]:
+            cur.append(coins[1])
+        if 3 in res[0]:
+            cur.append(coins[2])
+        return cur[0], cur[1]
+    elif len(coins) in [4, 5]:
+        res = guess([coins[0]], [coins[1]], [coins[2]])
+        assert len(res) != 3
+        if len(res) == 1:
+            return solve_2(coins[3:])
+        cur = []
+        if 1 in res[0]:
+            cur.append(coins[0])
+        if 2 in res[0]:
+            cur.append(coins[1])
+        if 3 in res[0]:
+            cur.append(coins[2])
+        if len(cur) == 1:
+            cur.append(solve_1(coins[3:]))
+        return cur[0], cur[1]
+
+    # At least 6, so 3*coin4 <= len
+    amount = (len(coins)+2) // 4
+
+    coin1 = coins[:amount]
+    coin2 = coins[amount:2*amount]
+    coin3 = coins[2*amount:3*amount]
+    coin4 = coins[3*amount:]
+    res = guess(coin1, coin2, coin3)
+    assert len(res) != 3 # 3 Distinct weights doesn't make sense with 2 coins
+    if len(res) == 2:
+        # There is an imbalance.
+        if len(res[0]) == 2:
+            # There are 2 heavy piles and 1 light pile
+            # 1-1-0
+            cur = []
+            if 1 in res[0]:
+                cur.append(solve_1(coin1))
+            if 2 in res[0]:
+                cur.append(solve_1(coin2))
+            if 3 in res[0]:
+                cur.append(solve_1(coin3))
+            return cur[0], cur[1]
+        else:
+            # There is 1 heavy pile and 2 light piles
+            # 2-0-0-0, or 1-0-0-1
+            if res[0][0] == 1:
+                weighted_first = coin1 + coin2 + coin3
+            elif res[0][0] == 2:
+                weighted_first = coin2 + coin3 + coin1
+            elif res[0][0] == 3:
+                weighted_first = coin3 + coin1 + coin2
+
+            weighted = weighted_first[:len(coin4)]
+            empty = weighted_first[len(coin4):2*len(coin4)]
+            res2 = guess(weighted, empty, coin4)
+
+            assert len(res2) == 2
+            if len(res2[0]) == 2:
+                return solve_1(weighted), solve_1(coin4)
+            else:
+                return solve_2(weighted)
+    else:
+        # 0-0-0-2
+        return solve_2(coin4)
+

Now, to solve the 3 coin case, let’s divide our coins into 3 piles of size $a$, plus the remainder.

Let’s do the case analysis for different outcomes of the weighing.

  • If the outcome of the weighing has 3 distinct bands of weight (like 3 > 1 > 2), then we know the heaviest pile has 2 fake coins, and the middle pile has 1 fake coin.
    • Final complexity: $1 + \log_4(a) + \log_2(a) = 3\log_4(a)$
  • If the outcome of the weighing has 2 distinct bands of weight, with two heavier piles (3 1 > 2), then both heavy piles have 1 fake coin, and the remainder has 1 fake coin.
    • Final complexity: $1 + 2\log_4(a) + \log_4(n-3a)$
  • If the outcome of the weighing has 2 distinct bands of weight, with two lighter piles (3 > 1 2), then the heavy pile has anywhere from 1 to 3 fake coins, and the remainder has anywhere from 0 to 2 fake coins.
    • This can simply be solved by recursing to find 3 coins in the heavy pile plus the remainder in $1 + T(n-2a)$
  • If the outcome of the weighing has 1 distinct band of weight (all equal), then either all piles have a fake coin, or the remainder has all 3 fake coins
    • We can disambiguate this by weighing the entire remainder against a subset of the weighed piles, giving a complexity of $2 + \text{max}(3\log_4(a), T(n-3a))$

However, you’ll notice the remainder causes some issues in the final case, and our logic can be made much simpler if we just make each weighed pile about $n/3$ in size. Then in the final case, all 3 fake coins being in the remainder is impossible!

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+
def solve_3(coins):
+    if len(coins) == 3:
+        return coins[0], coins[1], coins[2]
+    elif len(coins) == 4:
+        res = guess([coins[0]], [coins[1]], [coins[2]])
+        if len(res) == 1:
+            return coins[0], coins[1], coins[2]
+        else:
+            if 1 in res[1]:
+                return coins[1], coins[2], coins[3]
+            if 2 in res[1]:
+                return coins[0], coins[2], coins[3]
+            if 3 in res[1]:
+                return coins[0], coins[1], coins[3]
+    amount = len(coins) // 3
+    coin1 = coins[:amount]
+    coin2 = coins[amount:2*amount]
+    coin3 = coins[2*amount:3*amount]
+    coin4 = coins[3*amount:]
+
+    res = guess(coin1, coin2, coin3)
+    if len(res) == 3:
+        # 2-1-0-0
+        cur = []
+        if res[0][0] == 1:
+            cur.extend(solve_2(coin1))
+        if res[0][0] == 2:
+            cur.extend(solve_2(coin2))
+        if res[0][0] == 3:
+            cur.extend(solve_2(coin3))
+        if res[1][0] == 1:
+            cur.append(solve_1(coin1))
+        if res[1][0] == 2:
+            cur.append(solve_1(coin2))
+        if res[1][0] == 3:
+            cur.append(solve_1(coin3))
+        return cur[0], cur[1], cur[2]
+    elif len(res) == 2:
+        if len(res[0]) == 2:
+            # 1-1-0-1
+            cur = []
+            if 1 in res[0]:
+                cur.append(solve_1(coin1))
+            if 2 in res[0]:
+                cur.append(solve_1(coin2))
+            if 3 in res[0]:
+                cur.append(solve_1(coin3))
+            cur.append(solve_1(coin4))
+            return cur[0], cur[1], cur[2]
+        else:
+            # 3-0-0-0
+            # 2-0-0-1
+            # 1-0-0-2
+            cur = coin4
+            if res[0][0] == 1:
+                cur.extend(coin1)
+            if res[0][0] == 2:
+                cur.extend(coin2)
+            if res[0][0] == 3:
+                cur.extend(coin3)
+            # log_3(n)
+            return solve_3(cur)
+    else:
+        # 0-0-0-3 - not possible due to definition of amount.
+        # 1-1-1-0
+        return solve_1(coin1), solve_1(coin2), solve_1(coin3)
+

Complexity: $\mathcal{O}(3\log_4(n))$ guesses

Cutting Board 1

These next two problems invite you to think about optimal strategies in a game of cuts.

Hint 1

Try to classify some small games as one of the four outcomes, try to make some rules for combining 2 boards.

Hint 2

  1. Can the game ever have a strategy where the 1st player alyways wins?
  2. Is it just boards with length = width where the 2nd player alyways wins?

Solution

Let’s try to analyse the smallest few games, and in doing so make some rules for combining boards together.

We’ll call a game $2$ if the second player wins, $1$ if the first player wins, and $V$ or $H$ for Vaughn/Hazel winning always.

 12345678910
12V        
2H         
3          
4          
5          
6          
7          
8          
9          
10          

The $1\times 1$ game is super simple - the first player can’t move, so the second player always wins. For the $2\times 1$ and $1\times 2$ games, only one player has a move to make, so they always win.

Let’s look at a few more games.

 12345678910
12VVVVVVVVV
2H22       
3H2        
4H         
5H         
6H         
7H         
8H         
9H         
10H         

First off, any $n\times 1$ or $1\times n$ game handles exactly the same as a $2\times 1$.

Next, for the $2\times 2$, note that whoever moves first will create two games that we’ve previously calculated they cannot win. Playing a game on two boards which individually the other player has a strategy to win is a loss for the starting player, because the responding player always has a winning move on both boards, provided they always play on the same board as the previous player’s move.

Additionally, for $3\times 2$ and $2\times 3$, note that the game will always become a combination of a $2\times 2$ and a $1\times 2$/$2\times 1$ game.

We’ve come up with the following two rules for cutting board (assuming that these 2 boards are the best the players can come up with). These rules also apply to Hazel when the outcomes are flipped.

Rules for cutting board

There is one more rule that comes up when analysing $2\times 4$. Note that while Vaughn could split into $2\times 1$ and $2\times 3$, this would result in a loss (As our $2+H$ rule states). Instead, Vaughn can split the game into $2\times 2$ and $2\times 2$. Since both games are losing for the second player, Vaughn can just follow whatever board Hazel makes a move on, and Vaughn will always win the game (If Hazel makes a move on the first $2\times 2$ box, then Vaughn has a winning move on one of the resultant cutting boards).

More Rules for cutting board

With just these three rules in hand, we can actually fill the entire board:

 12345678910
12VVVVVVVVV
2H22VVVVVVV
3H22VVVVVVV
4HHH2222VVV
5HHH2222VVV
6HHH2222VVV
7HHH2222VVV
8HHHHHHH222
9HHHHHHH222
10HHHHHHH222

Hopefully by now you’re noticing the pattern. A proof left for the reader is why these 2s appear in boxes of size $2^k$. (Hint: Think about the first value in the row that can be a 2 rather than a H. What does it require in the values above it in the column? And what about the first value in the row that is a V, what needs to precede the V in the same row?)

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
import math
+
+n, m = list(map(int, input().split()))
+
+l2n, l2m = math.floor(math.log2(n)), math.floor(math.log2(m))
+
+if l2n == l2m:
+    print("2nd Player")
+elif l2n > l2m:
+    print("Vaughn")
+else:
+    print("Hazel")
+

Cutting Board 2

Hint 1

If you’ve seen the solution for Cutting Board 1 - try a similar approach of mapping out the first few values in both dimensions. You should see a very different picture.

Hint 2

Notice that:

  • Adding a cut will always take the game into $n$ copies of the same board, which will either be, $2$, V or H.
  • Multiple games of $2$ are just $2$, Multiple games of V or H are just V or H.

As such, $2\times 2$, $2\times 3$, $2\times 5$ are essentially the same board, as far as this game is concerned. How is $2\times 4$ different?

Solution

As noted in the previous hint, let’s use the rules of combining boards to map out some smaller values:

 12345678910
12VVVVVVVVV
2H22V2V2VVV
3H22V2V2VVV
4HHH2H2HV22
5H22V2V2VVV
6HHH2H2HV22
7H22V2V2VVV
8HHHHHHH2HH
9HHH2H2HV22
10HHH2H2HV22

This table is a lot harder to decipher, but notice what the table looks like when I change the order of rows:

 12345678910
12VVVVVVVVV
2H22V2V2VVV
3H22V2V2VVV
5H22V2V2VVV
7H22V2V2VVV
4HHH2H2HV22
6HHH2H2HV22
9HHH2H2HV22
10HHH2H2HV22
8HHHHHHH2HH

We see strong bands of equal results. In a sense, all prime sized boards are equivalent, as are all boards of size 2 prime factors, and so on. Let’s continue this logic and permute the columns:

 12357469108
12VVVVVVVVV
2H2222VVVVV
3H2222VVVVV
5H2222VVVVV
7H2222VVVVV
4HHHHH2222V
6HHHHH2222V
9HHHHH2222V
10HHHHH2222V
8HHHHHHHHH2

In general, the best strategy seems to be: Cut out a prime factor of your board size, and you are left with multiple boards that will be best for you.

Note that

  • If this produces winning boards for the opposite team, there was no way for you to win.
  • If this produces winning boards for your team, then you can win simply by following whichever game your opponent plays first on.
  • If this produces winning boards for the second player, then you can win simply by following whichever game your opponent plays first on.

Therefore the solution boils down to finding the number of prime factors a number has.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+
MAX_N = int(2e6)
+
+is_prime = [True] * (MAX_N+1)
+is_prime[0] = False
+is_prime[1] = False
+
+for jump in range(2, MAX_N+1):
+    if not is_prime[jump]: continue
+    for pos in range(2*jump, MAX_N+1, jump):
+        is_prime[pos] = False
+
+primes = [i for i, v in enumerate(is_prime) if v]
+
+def n_prime_factors(v):
+    n_factors = 0
+    for p in primes:
+        while v % p == 0:
+            v //= p
+            n_factors += 1
+    return n_factors
+
+n, m = list(map(int, input().split()))
+
+n_factors = n_prime_factors(n)
+m_factors = n_prime_factors(m)
+
+if n_factors > m_factors:
+    print("Vaughn")
+elif m_factors > n_factors:
+    print("Hazel")
+else:
+    print("2nd Player")
+

Complexity: $\mathcal{O}(n)$

Divisors 0

Hint 1

Is there a formula we could be using that simplifies the sum of the first $n$ natural numbers?

If so, how would we change this formula for modulo?

Hint 2

Note that since we take the modulo of every individual value, the modulo-ed sequence repeats every $m$ values, so rather than computing the entire sequence, we can compute the sum of the first $m$ values and, excluding the final $n \% m$ bit of the sequence, we can simply count the number of repetitions.

Solution

As noted in the hint, the sequence repeats if we look at for example $n=14$, $m=4$.

\[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14\]

after modulo by 4 becomes

\[1 + 2 + 3 + 0 + 1 + 2 + 3 + 0 + 1 + 2 + 3 + 0 + 1 + 2\]

Note that the $1 + 2 + 3 + 0$ sum is repeated a bunch of times, except for the final value $ + 1 + 2$.

Using the triangle number formula, the sum $1 + 2 + 3 + 0$ is equal to $\frac{3\times (3+1)}{2} = 6$, and this sequence is repeated $\lfloor \frac{14}{m} \rfloor = 3$ times.

So the total sum is equal to $3 \times 6 + 1 + 2$, however this final bit can be computed as $\frac{(n \% m)\times((n \% m) + 1)}{2} = 3$

1
+2
+3
+4
+5
+6
+7
+8
+9
+
n, m = list(map(int, input().split()))
+
+triangle = (m * (m-1)) // 2
+
+total = triangle * (n // m)
+extra = n % m
+total += (extra * (extra + 1)) // 2
+
+print(total)
+

Divisors 1

Hint 1

Try to figure out a rule for what natural number $n$ will generate the value $a_b$ in the sequence.

Note that $1_a$ will always be generated by natural number $a$.

Hint 2

Hopefully you’ve figured out that number $a_b$ will be generated by the natural number $a \times b$.

As such, ordering by appearance in the sequence should just be ordering by $a\times b$, with some care needing to be taken when comparing $a_b$ with $c_d$ and $a\times b = c\times d$.

Solution

As mentioned in the hint, the value $a_b$ is generated by the natural number $a \times b$, and so ordering the individual values by the natural number which generates them should sort the sequence in order.

In the case where $a \times b = c \times d$, notice that the smaller divisor will always be included in the sequence first, so after comparing $a\times b$ against $c\times d$, we need only compare $a$ against $c$.

1
+2
+3
+4
+5
+6
+7
+8
+
nums = int(input())
+v_and_p = list(map(lambda x: list(map(int, x.split("_"))), input().split()))
+# Sort by (a*b, a) (And retain b so we can reconstruct the sequence)
+sort_keys = list(map(lambda x: (x[0]*x[1], x[0], x[1]), v_and_p))
+sort_keys.sort()
+
+formatted = list(map(lambda x: f"{x[1]}_{x[2]}", sort_keys))
+print(" ".join(formatted))
+

Divisors 2

Hint 1

Try to flip the problem on its head a bit and solve the case of counting how many 1s, 2s, 3s, etc. occur before the value you are looking for.

For example, 2 occurs 7 times before $3_5$.

Hint 2

For a natural number $n$, the value $a$ appears in the sequence before $n_1$ $\lfloor \frac{n}{a} \rfloor$ times.

This is all well and good for small $a$, but we can’t have a linear solution for this problem. You need to make use of the fact that for large $a$ (In particular, $a > \sqrt{n}$), the value of $\lfloor \frac{n}{a} \rfloor$ is always rather small (In particular $\lfloor \frac{n}{a} \rfloor \leq \sqrt{n}$)

Solution

To start with, let’s assume that we want to find the index of $n_1$ for some $n$ (The end of the sequence of divisors of $n$), since this will make our lives a bit easier.

Notice that for any value $a$, $a$ will occur in the sequence before $n_1$ $\lfloor \frac{n}{a} \rfloor$ times. Let’s graph this for a large enough $n$:

Graph of n/a

This graph has a lot of large unique values for $a \leq \sqrt{n}$, and a few smaller common values for $a \geq \sqrt{n}$ (You can argue this via pidgeonhole principle - there are $\sqrt{n}$ possible values)

As such, we can count the first $\sqrt{n}$ values ourselves, and then count “sections” of the graph that are of equal size up to and including $\sqrt{n}$ in height.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+
import math, sys
+def val(inp):
+    special, noccurences = list(map(int, inp.split("_")))
+
+    # This occurs for number n*k.
+
+    nk = special * noccurences
+    # Find the index of (nk-1)_1 - Then we can just add the divisors of nk up until special.
+    nk -= 1
+
+    # Notice that nk // j can only be a few different values (nk, nk/2, nk/3 already is much smaller after 3 iterations)
+    # We can instead find, for i up until sqrt(n):
+    # All j such that nk//j = i
+    # Then simply compute nk//i for all remaining (small) i.
+
+    root = math.floor(math.sqrt(nk))
+
+    print("nk", nk, file=sys.stderr)
+    print("root", root, file=sys.stderr)
+
+    nums = 0 # We start at index 1.
+    for i in range(1, root+1):
+        # What j satisfy nk//j=i?
+        if i == 1:
+            nums += nk - nk//2
+            prev = nk // 2
+            continue
+        # Well, anything where i * j <= nk < (i+1)*j
+        # In other words, start at nk//(i+1)
+        # Ends when the previous barrier is hit
+        smallest_excl = nk // (i+1)
+        largest_incl = prev
+        prev = smallest_excl
+        nums += (largest_incl - smallest_excl) * i
+        print(f"nk // j = {i} for ({smallest_excl}, {largest_incl}]", file=sys.stderr)
+    # Now we need to find the rest
+    for i in range(1, root+1):
+        # Exclude the final entry for anything larger than special.
+        if nk // i <= root:
+            break
+        nums += nk // i
+
+    nk += 1
+
+    root = math.floor(math.sqrt(nk))
+
+    # Now we just need to add position for the final integer.
+    if special * special < nk:
+        # Simply count
+        for i in range(1, special + 1):
+            if nk % i == 0:
+                nums += 1
+    else:
+        # Count total
+        tot_turn = 0
+        for i in range(1, root+1):
+            if nk % i == 0:
+                tot_turn += 1
+        tot_turn *= 2
+        if root * root == nk:
+            tot_turn -= 1
+        for i in range(1, root+1):
+            if nk%i == 0 and nk // i > special:
+                tot_turn -= 1
+            elif nk%i == 0:
+                break
+        nums += tot_turn
+
+    if nk == 1:
+        # Previous doesn't work for base case
+        return 1
+    else:
+        return nums
+
+print(val(input()))
+

This however has a much more elegant solution, found by team de in the competition. Looking at the graph again, the graph is entirely the same when flipped along the axis $y=x$. So rather than counting the $\leq\sqrt{n}$ and $\geq \sqrt{n}$ cases separately, simply take the $\leq \sqrt{n}$ part of the graph, and double it.

Graph of n/a

This value then only double counts in the $\sqrt{n} \times \sqrt{n}$ box in the bottom left, which we can then subtract:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+
# Courtesy of `de`.
+a,b = map(int,input().split("_"))
+n = a*b
+m = int((n-1)**0.5)
+# number of values for i <= sqrt(n-1)
+tot = 0
+for i in range(1,m+1):
+    tot += (n-1)//i
+
+def numDivisor(i,n):
+    tot = 0
+    if i*i <= n:
+        for j in range(1,i+1):
+            if (n%j) == 0:
+                tot += 1
+        return tot
+
+    j = n/i
+    m = int(n**0.5)
+    numFacs = 0
+    smth = 0
+    for k in range(1,m+1):
+        if (n%k == 0):
+            numFacs += 1
+            if k < j:
+                smth += 1
+
+    numFacs *= 2
+    if m*m == n:
+        numFacs -= 1
+
+    return numFacs - smth
+
+
+# double the area, subtract the square in the bottom left (m*m), and then add the divisors of just n up until a.
+tot = 2*tot - m*m + numDivisor(a,n)
+print(tot)
+

Divisors 3

A bit of a departure from all other problems in the contest, this problem asks you to approximate a sequence efficiently and effectively.

This problem ended up being a bit of a bad fit for a competition because a guarantee of maximum error is not the same as a practical guarantee of maximum error. Additionally, the accuracy and magnitude of the result was rather restrictive and made the problem a bit more annoying than it should have been.

Additionally, I didn’t do enough due diligence on checking my results, which made my initial solution incorrect (albeit accidentally accurate enough for the judge miraculously)

Hint 1

To solve the first boundary ( $\ln(n)$ ), you can solve this with a single line of code. (Moreso just a formula, than a line of code)

Also, I forgot to notice this in competition, but you’ll likely need an external package for extra decimal precision, like Pythons decimal package.

Hint 2

The problem bounds imply that $\sqrt{n}$ should somehow come into play. Is there a way we count the contributions of $\frac{1}{a}$ for $a <= \sqrt{n}$ differently from all other $\frac{1}{a}$?

Solution

First, let’s solve the first test set bound.

Notice that, just like in the previous problem, for an end value $n$, and $a \leq n$, the value $\frac{1}{a}$ will be in the sequence $\lfloor\frac{n}{a}\rfloor$ times.

Therefore we can over-estimate the contribution for $\frac{1}{a}$ in total as $\frac{1}{a} \times \frac{n}{a} = \frac{n}{a^2}$.

Summing over all $a$, we get the following sequence, which is a rather famous sequence:

\[\frac{n}{1^2} + \frac{n}{2^2} + \frac{n}{3^2} + \cdots + \frac{n}{n^2} = n (\frac{1}{1^2} + \frac{1}{2^2} + \frac{1}{3^2} + \cdots + \frac{1}{n^2}) \approx n \frac{\pi^2}{6}\]

The error bound on the approximation of the sum of reciprocals is $\frac{1}{n}$, meaning that ignoring the error that removing the floor contributes, we are within $\frac{n}{n} = 1$ of the correct solution. However removing the floor can add as much as $\ln(n)$ to the result.

To solve the second test set bound, there was one intended solution, which didn’t end up actually ensuring the error bounds were met, and another solution that was found by team de. We’ll start with the semi-faulty solution.

Solution 1 - Modifying the test set 1 sequence.

Notice that the estimation error from $\frac{1}{a}\lfloor \frac{n}{a} \rfloor$ to $\frac{n}{a^2}$ is $\frac{n \% a}{a^2}$.

Let’s look at the full error expression for $n=20$:

\[\text{err} = \frac{0}{1^2} + \frac{0}{2^2} + \frac{2}{3^2} + \frac{0}{4^2} + \frac{0}{5^2} + \frac{2}{6^2} + \frac{6}{7^2} + \frac{4}{8^2} + \frac{2}{9^2} + \frac{0}{10^2} + \frac{9}{11^2} + \frac{8}{12^2} + \frac{7}{13^2} + \frac{6}{14^2} + \frac{5}{15^2} + \frac{4}{16^2} + \frac{3}{17^2} + \frac{2}{18^2} + \frac{1}{19^2}\]

Notice that there are bands of rather well behaved fractions, for example from denominator 11 to 19. In general there will be an arithmetic progression on the numerators between the denominators of $\frac{n}{a+1}$ and $\frac{n}{a}$. Let’s try creating an estimator for these kinds of sequences.

\[R := \frac{a + bc}{x^2} + \frac{a+b(c-1)}{(x+1)^2} + \frac{a+b(c-2)}{(x+2)^2} + \cdots + \frac{a}{(x+c)^2}\]

This sequence would be easier to resolve if the numerators increased with the denominators, rather than the opposite direction, so let’s do a manipulation.

\[(x + \frac{a}{b} + c) \times (\frac{1}{x^2} + \frac{1}{(x+1)^2} + \frac{1}{(x+2)^2} + \cdots + \frac{1}{(x+c)^2}) - \frac{R}{b} = \frac{x}{x^2} + \frac{x + 1}{(x+1)^2} + \frac{x + 2}{(x+2)^2} + \cdots + \frac{x+c}{(x+c)^2}\]

Both sequences above have well known approximations, shown below:

\[\frac{1}{1^2} + \frac{1}{2^2} + \frac{1}{3^2} + \cdots + \frac{1}{n^2} = \frac{\pi^2}{6} - \frac{1}{n} - [0, \frac{1}{(n)(n+1)}]\] \[\frac{1}{1^2} + \frac{2}{2^2} + \frac{3}{3^2} + \cdots + \frac{n}{n^2} = \frac{1}{1} + \frac{1}{2} + \frac{1}{3} + \cdots + \frac{1}{n} = \ln(n) + \gamma + \frac{1}{2n} - [0, \frac{1}{8n^2}]\]

where $\gamma$ is a constant. Substituting this into the equation above gives:

\[(x + \frac{a}{b} + c) \times (\frac{\pi^2}{6} - \frac{1}{x+c} - [0, \frac{1}{(x+c)(x+c+1)}] - \frac{\pi^2}{6} + \frac{1}{x-1} + [0, \frac{1}{(x-1)(x)}]) - \frac{R}{b} = \ln(x+c) + \gamma + \frac{1}{2(x+c)} - [0, \frac{1}{8(x+c)^2}] - \ln(x-1) - \gamma - \frac{1}{2(x-1)} + [0, \frac{1}{8(x-1)^2}]\] \[(x + \frac{a}{b} + c) \times (\frac{1}{x-1} - \frac{1}{x+c} + [-\frac{1}{(x+c)(x+c+1)}, \frac{1}{(x-1)(x)}]) - \frac{R}{b} = \ln(\frac{x+c}{x-1}) + \frac{1}{2(x+c)} - \frac{1}{2(x-1)} + [-\frac{1}{8(x+c)^2}, \frac{1}{8(x-1)^2}]\]

Which solving for $R$ gives us

\[R = (xb + a + bc) \times (\frac{1}{x-1} - \frac{1}{x+c}) - b \times (\ln(\frac{x+c}{x-1}) + \frac{1}{2(x+c)} - \frac{1}{2(x-1)})\]

with an error bound at most $\frac{xb + a + bc}{(x-1)(x)} + \frac{b}{8(x-1)^2}$.

Let’s use this estimate for the denominators $11$ through to $19$. This has $b=1$, $x=11$, $c=8$, $a=1$:

\[R = (11 + 1 + 8) \times (\frac{1}{10} - \frac{1}{19}) - (\ln(\frac{19}{10}) + \frac{1}{38} - \frac{1}{20})\]

which gives about $0.07$ off of the actual solution

Choosing $d$ from $1$ up until $m := \lfloor \sqrt{n} \rfloor$ we can look at the denominator range $\frac{n}{d}$ down to $\frac{n}{d+1}$.

This has $b = d$, $c = \lfloor\frac{n}{d}\rfloor - \lfloor\frac{n}{d+1}\rfloor - 1$, $x = \lfloor\frac{n}{d+1}\rfloor$, and $a = n \% \lfloor \frac{n}{d} \rfloor$.

\[R = (d \lfloor \frac{n}{d+1} \rfloor + (n \% \lfloor \frac{n}{d} \rfloor ) + d \times (\lfloor\frac{n}{d}\rfloor - \lfloor\frac{n}{d+1}\rfloor - 1)) \times (\frac{1}{\lfloor\frac{n}{d+1}\rfloor -1} - \frac{1}{\lfloor\frac{n}{d}\rfloor - 1}) - d\times (\ln(\frac{\lfloor\frac{n}{d}\rfloor - 1}{\lfloor\frac{n}{d+1}\rfloor-1}) + \frac{1}{2(\lfloor\frac{n}{d}\rfloor - 1)} - \frac{1}{2(\lfloor\frac{n}{d+1}\rfloor-1)})\] \[R = (d \times (\lfloor\frac{n}{d}\rfloor - 1) + (n \% \lfloor \frac{n}{d} \rfloor )) \times \frac{\lfloor\frac{n}{d}\rfloor - \lfloor\frac{n}{d+1}\rfloor}{(\lfloor\frac{n}{d}\rfloor - 1) \times (\lfloor\frac{n}{d+1}\rfloor - 1)} - d\times (\ln(\frac{\lfloor\frac{n}{d}\rfloor - 1}{\lfloor\frac{n}{d+1}\rfloor-1}) + \frac{\lfloor\frac{n}{d+1}\rfloor - \lfloor\frac{n}{d}\rfloor}{2(\lfloor\frac{n}{d}\rfloor - 1)\times (\lfloor\frac{n}{d+1}\rfloor-1)})\] \[R = (n - d) \times \frac{\lfloor\frac{n}{d}\rfloor - \lfloor\frac{n}{d+1}\rfloor}{(\lfloor\frac{n}{d}\rfloor - 1) \times (\lfloor\frac{n}{d+1}\rfloor - 1)} - d\times (\ln(\frac{\lfloor\frac{n}{d}\rfloor - 1}{\lfloor\frac{n}{d+1}\rfloor-1}) + \frac{\lfloor\frac{n}{d+1}\rfloor - \lfloor\frac{n}{d}\rfloor}{2(\lfloor\frac{n}{d}\rfloor - 1)\times (\lfloor\frac{n}{d+1}\rfloor-1)})\]

Although in practice I found

\[R = 1 - d \times \ln(\frac{d+1}{d})\]

A relatively good and simple estimator for the above. (But the solution will use the lengthy approximation)

What is the error in this approximation? Well, there ends up being lots of cancellations in errors, since we are combining together lots of chained approximations, and so what was a positive error in the previous step now becomes the same negative error (this is not true for all error, for example some of the error in the harmonic approximation, but it is true for some).

Unless I’ve screwed something up (very possible) the total error ends up being a small factor of $\frac{1}{\sqrt{n}}$. This seems to atleast be true in practice.

For the values $\frac{n \% c}{c^2}$ for $c \leq \sqrt{n}$, we can just compute those manually.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+
import sys
+import math
+from decimal import Decimal, getcontext
+
+getcontext().prec = 50
+
+n = int(input())
+
+# Subtract 1 for the trail off
+res = Decimal(n) * Decimal(math.pi) * Decimal(math.pi) / Decimal(6) - 1
+
+print("Pretty good approximation:", res, file=sys.stderr)
+
+ceil = min(n, int(1e6))
+
+# Now, we need to reduce by a%d/d^2 for all d <= a.
+for d in range(1, n // ceil):
+    d = Decimal(d)
+    smol = Decimal(n // (d + 1))
+    beeg = Decimal(n // d)
+    first_part = d * smol + (n % beeg) + d * (beeg - smol - 1)
+    second_part = Decimal(1) / Decimal(smol - 1) - Decimal(1) / Decimal(beeg - 1)
+    third_part = Decimal.ln((beeg - 1) / (smol - 1)) + Decimal(1) / (2 * (beeg - 1)) - Decimal(1) / (2 * (smol - 1))
+
+    reduction = first_part * second_part - d * third_part
+    # print(f"1/{n//d}^2 + ... + {n//(d+1)}/{n//(d+1)}^2 = {reduction}")
+    res -= reduction
+
+# Below sqrt(a), we can manually subtract the value
+for d in range(2, ceil):
+    res -= Decimal(n % d) / Decimal(d*d)
+
+print("Better:", res, file=sys.stderr)
+
+print(res)
+

Solution 2 - Other approximations

This solution was found by team de in competition.

Rather than sticking with the $\frac{n\pi^2}{6}$ approximation, this solution instead goes back to the original sequence and looks at it with a new viewpoint:

Let’s collect all of the $\frac{1}{1}s$, $\frac{1}{2}s$, and so on, in distinct columns, where the height of the column represents how many times that fraction is used.

Reciprocal fractions

We can sum the columns before and after $m = \lfloor \sqrt{n} \rfloor$ differently.

For those before $m$, we can simply find each column’s contribution by adding $a \times \lfloor\frac{n}{a}\rfloor$. For those after $m$, notice that $\lfloor \frac{n}{a} \rfloor$ will only take at most $m+1$ unique values (Since $\lfloor \frac{n}{m} \rfloor \leq m+1$), and in fact. This means that if, rather than summing by column, we instead sum by row, we’ll have only $m$ sets of values to compute, rather than $n-m$.

It’s worth noting that before $m$, we have $m$ distinct columns, and so after $m$, we have $m$ distinct rows (subject to off by one issues)

What do our rows of the graph look like? Well, using the previous image, every row (From column $m$ onwards), will be a sum of consecutive reciprocals up until some point. For example, for $n=9$, $m=3$, we have:

\[\frac{1}{3} +\] \[\frac{1}{3} + \frac{1}{4} +\] \[\frac{1}{3} + \frac{1}{4} + \frac{1}{5} + \frac{1}{6} + \frac{1}{7} + \frac{1}{8} + \frac{1}{9}.\]

Now each of these rows we can use the approximation $\frac{1}{1} + \frac{1}{2} + \frac{1}{3} + \ldots + \frac{1}{n} = \ln(n) + \gamma + \frac{1}{2n} + \mathcal{O}(\frac{1}{n^2})$.

Applying this gives us:

\[\ln(\frac{3}{2}) + \frac{1}{6} - \frac{1}{4} + \mathcal{O}(\frac{1}{m^2}) +\] \[\ln(\frac{4}{2}) + \frac{1}{8} - \frac{1}{4} + \mathcal{O}(\frac{1}{m^2}) +\] \[\ln(\frac{9}{2}) + \frac{1}{18} - \frac{1}{4} + \mathcal{O}(\frac{1}{m^2})\]

So we can use this to solve the problem with a total error bound of $\mathcal{O}(\frac{m}{m^2}) = \mathcal{O}(\frac{1}{m})$!

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
# Rephrased version of team `de`s solution.
+from decimal import Decimal, getcontext
+
+getcontext().prec = 50
+
+n = int(input())
+
+m = max(1,int(n**0.5))
+m1 = n//m
+
+total = Decimal("0")
+
+# Handle the first m columns
+for i in range(1, m+1):
+    total += Decimal(n//i)/Decimal(i)
+# Handle the remaining m1 rows
+for i in range(1, m1+1):
+    total += Decimal.ln(Decimal(n//i) / Decimal(m)) + Decimal(1) / Decimal(2 * (n//i)) - Decimal(1) / Decimal(2 * m)
+
+print(total)
+

Lights 1

Hint 1

Simulating the problem takes $\mathcal{O}(n\ln(n))$ time. Too much - there actually aren’t many lights that will be turned on, and we can generate them in a neat sequence.

Hint 2

Consider a (faulty) proof that no light should be turned on. What is wrong with it, and what does this tell us about the solution?

Consider any light $n$. Take any factor of $n$, call it $a$. Note that $\frac{n}{a}$ will be another distinct factor of $n$ - This is true for all $a$ we could have chosen. Since this is the case (every factor has a unique pair), the total number of factors of $n$ is even. Therefore light $n$ is off.

Solution

The issue with the proof given in Hint 2, is that for square numbers, the “pairing” maps the square root of a number with itself. Take $36$ for example, the divisors $1, 2, 3$ are paired with $36, 18, 12$ respectively, but $6$ is its own pair.

In fact, square numbers are the only numbers for which the proof given in Hint 2 doesn’t work, for this very reason. So the problem really boils down to counting how many square numbers are less than $n$. We can do this easily by simply returning $\lfloor \sqrt{n} \rfloor$!

1
+2
+3
+4
+5
+6
+
import math
+
+n = int(input())
+
+print(math.floor(math.sqrt(n)))
+
+

Lights 2

Hint 1

Note importantly that if Robot $a$ flicks a light switch, then Robot $a-1$ also flicks the same switch.

Hint 2

Consider the first $2^i$ lights. How many have been flicked once? twice? three times?

Solution

Using Hint 1, what we really need to find are the lights which are flicked on by Robot 1, but not Robot 2 (those that are flicked once), the lights which are flicked on by Robot 3, but not Robot 4 (those that are flicked thrice), and so on.

The lights that are flicked on by Robot 1, but not Robot 2 are those which are divisible by $2^0=1$, but are not divisible by $2^1=2$.

For the first $n$ lights, exactly $\lfloor \frac{n+1}{2} \rfloor$ of them will satisfy this rule.

The lights that are flicked on by Robot 3, but not Robot 4 are those which are divisible by $2^2=4$, but are not divisible by $2^3=8$. If we floor divide $n$ by $4$, and call this $m$, there are $m$ numbers divisible by $4$. Divide all these numbers by $4$. Now the question is simply how many of these are divisible by $2$, rather than divisible by $8$! So this is just the same as the first Robot question.

In general, the number of odd-flicked lights will be:

\[\lfloor \frac{n+1}{2} \rfloor + \lfloor \frac{\lfloor \frac{n}{4} \rfloor + 1}{2} \rfloor + \lfloor \frac{\lfloor \frac{n}{16} \rfloor + 1}{2} \rfloor + \lfloor \frac{\lfloor \frac{n}{64} \rfloor + 1}{2} \rfloor + \ldots\]

Until this flooring starts giving 0 terms.

Showing the calculations with some more depth

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
n = int(input())
+
+total = 0
+cur_divisor = 1
+while True:
+    div_by_divisor = n // cur_divisor
+    not_div_by_2 = (div_by_divisor+1)//2
+    if not_div_by_2 == 0:
+        break
+    total += not_div_by_2
+    cur_divisor *= 4
+
+print(total)
+

Lights 3

Hint 1

Assuming you’ve solved Lights [I], this shouldn’t be too much of a stretch.

If a lighter $n$ is on in this configuration, what does it tell us about the divisors of $n$?

Hint 2

This problem statement counts the number of odd divisors of a number. For an odd number, how does this relate to the number of total divisors? For a number which has a prime factorisation including $2^i$, how does this relate to the number of total divisors?

Solution

One important tool we can use for this problem is the prime factorisation of a number. Take $12$ for example, it has a prime factorisation of $2^23^1$. Note that any divisor of 12 is created simply by setting the power of $2$ to be anything from $0, 1, 2$, and the power of $3$ to be anything from $0, 1$. ($1 = 2^03^0$, $6 = 2^13^1, \ldots$).

In general, if your prime factorisation is $a_1^{a_2}b_1^{b_2}c_1^{c_2}\cdots$, then your number has $(a_2+1)(b_2+1)(c_2+1)\cdots$ divisors, to account for all choices of the indicies.

Now, for odd numbers, any divisor is an odd divisor, so the same theory applies - only square numbers work.

But what about for evens? Take some number $n = 2^i3^j5^k$. This number has $(i+1)(j+1)(k+1)$ divisors, but the number of odd divisors is just the number of divisors where we picked the power of $2$ to be $2^0$.

Therefore the number of odd divisors of $n$ is $(j+1)(k+1)$. In other words, its the number of divisors of the odd number $3^j5^k$, which must be a square number.

So the only lights that should be on, are odd square numbers, and odd square numbers times a power of two.

Notice however, that since $2 \times 2$ is itself a square number, we can actually count all of the above numbers by simply counting all square numbers, and all square numbers times plain old 2. Take $2^3 \times 5^2$ for example, we can write this instead as $2 \times 10^2$.

We can count the number of squares, and the number of numbers which are two times a square simply with

\[\lfloor \sqrt{n} \rfloor + \lfloor \sqrt{\frac{n}{2}} \rfloor\]
1
+2
+3
+4
+5
+6
+
import math
+
+n = int(input())
+
+total = math.floor(math.sqrt(n)) + math.floor(math.sqrt(n//2))
+print(total)
+

Lights 4

Hint 1

The logic used in Lights 3 around how many divisors a number has will remain useful here:

In general, if your prime factorisation is $a_1^{a_2}b_1^{b_2}c_1^{c_2}\cdots$, then your number has $(a_2+1)(b_2+1)(c_2+1)\cdots$ divisors.

Hint 2

This problem needs a rather sophisticated prime counting function.

Solution

Let’s use the rule given in Hint 1 to try to come up with a way of figuring out if a light is on.

Since the number of divisors is equal to $(a_2+1)(b_2+1)(c_2+1)\cdots$, the number of divisors will be prime only when:

  • There is a single prime divisor of the number (Since $(a_2+1)(b_2+1)$ is already non-prime), and
  • $a_2+1$ is prime.

In other words, the prime factorisation of $n$ must be $p^i$, where $i+1$ is prime.

Now, we could compute this linearly using a prime sieve, however we need to be a bit faster than this. There’s actually a batched way that we could solve this.

Let’s first counting the number of values before $n$ which are represented as $p^1$ - This is just the number of primes before $n$. Next, we’ll count the number of values before $n$ which are represented as $p^2$ - This is just the number of primes that appear before $\sqrt{n}$ (Since squaring the left side gives a number we are looking for, and squaring the right side gives $n$).

In general, if $\pi$ is the prime counting function ($\pi(n)$ = number of primes at or before $n$), then we need to compute

\[\pi(n) + \pi(n^{1/2}) + \pi(n^{1/4}) + \pi(n^{1/6}) + \pi(n^{1/10}) + \cdots\]

Now we just need a fast prime counting function, luckily the wikipedia page for prime counting functions has some tools we can use to make a faster prime counting function, in particular following a link to The Meissel Lehmer Algorithm - you can see that there exists an optimised version that solves the problem in $\mathcal{O}(n^\frac{2}{3})$ time, however for our purposes we can just use some simple rules from the Meissel Lehmer algorithm and makes something sublinear.

The primary thing to note from the mention of the algorithm in the prime counting function page, and, the main page for the algorithm, is that for our purposes, picking $y = \sqrt{n}$ and $n = \pi(y)$, then computing $\pi(m) = \phi(m, n) + n - 1 - P_2(m, n)$ is easy, because $P_2(m, n)$ is 0.

So all that’s left is simply to implement the recursion of $\phi$ efficiently. We can use a sieve up to $10^6$ for fast computation for small numbers, and for larger results simply defer to recursion:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+
import sys
+import math
+
+sys.setrecursionlimit(int(1e5))
+
+n = int(input())
+
+# pi(n) + pi(sqrt(n)) + pi(n^1/4) + ...
+
+prime_limit = int(3e6)
+
+is_prime = [True] * (prime_limit+1)
+pi = [0] * (prime_limit+1)
+primes = []
+is_prime[0] = False
+is_prime[1] = False
+for x in range(2, prime_limit+1):
+    pi[x] = pi[x-1]
+    if not is_prime[x]: continue
+    pi[x] += 1
+    primes.append(x)
+    for pos in range(2*x, prime_limit+1, x):
+        is_prime[pos] = False
+
+def phi(m, n):
+    if m <= prime_limit and pi[m] <= n:
+        return 1
+    if n == 0:
+        return math.floor(m)
+    return phi(m, n-1) - phi(m//primes[n-1], n-1)
+
+def fast_prime(n):
+    m = n
+    y = math.floor(math.sqrt(m))
+    n = pi[y]
+    return phi(m, n) + n - 1
+
+total = 0
+for x in range(1, math.floor(math.log2(n)) + 1):
+    if is_prime[x+1]:
+        total += math.floor(fast_prime(math.floor(math.pow(n, 1/x))))
+        print(x, total, file=sys.stderr)
+print(total)
+

One other optimisation that can be made is noticing that the recursion tree is often quite long with a lot of small branches (At some stage if dividing $n$ by any large prime $p$ will give you the base case, then we can use binary search to find the first prime which won’t hit the base case)

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+
import sys
+import math
+
+sys.setrecursionlimit(int(1e5))
+
+n = int(input())
+
+# pi(n) + pi(sqrt(n)) + pi(n^1/4) + ...
+
+prime_limit = int(3e6)
+
+is_prime = [True] * (prime_limit+1)
+pi = [0] * (prime_limit+1)
+primes = []
+is_prime[0] = False
+is_prime[1] = False
+for x in range(2, prime_limit+1):
+    pi[x] = pi[x-1]
+    if not is_prime[x]: continue
+    pi[x] += 1
+    primes.append(x)
+    for pos in range(2*x, prime_limit+1, x):
+        is_prime[pos] = False
+
+def phi(m, n):
+    if m <= prime_limit and pi[m] <= n:
+        return 1
+    if n == 0:
+        return math.floor(m)
+    # Try binary searching through a bunch of the easy to solve stuff.
+    if n > 50 and m > prime_limit and m//primes[n-1] <= prime_limit and 2*pi[m//primes[n-1]] <= n-1:
+        hi = n
+        lo = 10
+        while hi - lo > 2:
+            mid = (hi + lo) // 2
+            new_m = m//primes[mid]
+            if new_m <= prime_limit and 2*pi[new_m] <= mid:
+                # We can go lower
+                hi = mid + 1
+            else:
+                # We can't go this low
+                lo = mid + 1
+        # Skip from n to mid in n-mid steps, since all deductions will just be -1.
+        return phi(m, mid) - (n-mid)
+    return phi(m, n-1) - phi(m//primes[n-1], n-1)
+
+def fast_prime(n):
+    m = n
+    y = math.floor(math.sqrt(m))
+    n = pi[y]
+    return phi(m, n) + n - 1
+
+total = 0
+for x in range(1, math.floor(math.log2(n)) + 1):
+    if is_prime[x+1]:
+        total += math.floor(fast_prime(math.floor(math.pow(n, 1/x))))
+        print(x, total, file=sys.stderr)
+print(total)
+

Misc 0

Hint 1

It might be first good to simplify the fraction given to you, and seeing what you can do with this information.

Hint 2

If the simplified fraction of the problem is $\frac{c}{d}$, then at every integer time you’ll actually see all values of $\frac{x}{d}$ around the circle. So what does $d$ tell us about whether we hit the other side?

Solution

To quickly prove the result of Hint 2, notice that if the simplified fraction is $\frac{c}{d}$, then we know that $c$ and $d$ are coprime, in other words $\text{gcd}(c, d) = 1$. Then there exists some values $x$ and $y$ such that $cx + dy = 1$. Consider where we will be after $x$ seconds. We’ll be at $\frac{cx}{d} = \frac{1 - dy}{d} = \frac{1}{d} - y = \frac{1}{d}$ rotation around the circle (If $x$ is negative, just keep adding $d$ seconds until it is positive and you’ll get the same result). So in $x$ seconds we can move $\frac{1}{d}$ around the circle, and so in $x\times a$ seconds we can move to $\frac{a}{d}$ around the circle for any integer $a$. Hopefully it is relatively clear that for a simplified fraction of $\frac{c}{d}$, any rotation not expressible as $\frac{a}{d}$ is not possible after an integer amount of seconds.

Now, all we need to determine is whether we hit the opposite side of the circle, $\frac{1}{2}$. This is only possible (and always possible) if $\frac{1}{2}$ is expressible as $\frac{a}{d}$ for some $a$.

Which hopefully you can see is always possible if $d$ is divisible by $2$.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
a, b = list(map(int, input().split()))
+
+# Simple lcm work.
+for x in range(2, min(a, b)):
+    while a % x == 0 and b % x == 0:
+        a //= x
+        b //= x
+
+# 1/b is the jump size.
+if b % 2 == 0:
+    print("Other axis!")
+else:
+    print("Free!")
+

Misc 1

Hint 1

This problem is best viewed through the lens of recursion. Your recursion will likely need to look back at all previous values (I.E., parens(4) can be written as some combination of parens(3), parens(2), parens(1), parens(0))

Hint 2

Think about all possible parenthesis strings of containing $n$ closed parentheses. Each of these valid strings must start with an open parenthesis, which is closed at some point. What do I know about the strings in between these two parentheses, as well as after these two parentheses?

Solution

To answer Hint 2, the inside string and following string must both represent valid parenthesis strings!

Breakdown of a valid string

Therefore, we can construct a valid parenthesis string of length $n$ by deciding:

  1. How many parenthesis will occur inside the first closed parenthesis, call it $a$
  2. What is a valid parenthesis string of length $a$ to use inside
  3. What is a valid parenthesis string of length $n-a-1$ to use outside

And this informs our recursive counting function. To compute parens(n), simply:

  1. Iterate for all $a$ from 0 to $n-1$
  2. Compute parens(a) * parens(n-1-a)
  3. Add to the total and return the sum.

We just need to add some modular arithmetic to the solution and we are done:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
import sys
+
+sys.setrecursionlimit(int(1e5))
+
+MOD = int(1e9+7)
+
+n = int(input())
+
+DP = [None] * int(1e3 + 5)
+
+# How many parens patterns are there?
+def parens(n):
+    if DP[n] is not None:
+        return DP[n]
+    if n <= 1:
+        return 1
+    total = 0
+    for a in range(n):
+        # There are a parens in the first pattern
+        total += (parens(a) * parens(n-a-1)) % MOD
+        total %= MOD
+    DP[n] = total
+    return total
+
+print(parens(n))
+

Note - These are called the Catalan Numbers, and I was going to include many more problems featuring them originally. If you’re looking for a beautifully unique proof, look at Betrand’s Ballot Theorem, a generalisation of the Catalan numbers.

Misc 2

I guess I wasn’t thinking too much when I wrote this problem since it includes a data structure, but I think I count trees as more math than data structure, they are simply too fundamental :)

Hint 1

Try to think about the contributions on the left and right side of the removed edge separately (as well as that contributed by the edge itself separately). These three values when combined give you the answer.

Hint 2

The easiest of the three values to calculate is the amount contributed by the removed road itself.

This is simply the roads value, times the number of nodes on the left side of the road, times the number of nodes on the right side of the road. This is because left times right gives you the number of paths which cross the road.

The computation of the other two values (left road contributions, right road contributions) aren’t actually that much more complicated than above.

Solution

Let’s take a graph and remove some edge in the middle. First, we’ll try counting all contributions on the right side of the removed edge.

In fact, let’s be even more specific - let’s count the contributions on the right side of the removed edge, originating from paths starting at vertex $a$.

Drawing of an example tree

Counting all the paths, you’ll notice that “leaf” edges only contribute once, whereas the adjacent edges are counted multiple times - once for the shared node, and then once each for each adjacent leaf edge:

Drawing of all paths

We can write a recursive function to compute how many times each edge is counted, simply by counting how many nodes live below the edge. After computing this we can easily calculate the the total contribution by summing the contributed amount over each edge. Let’s call this result sumRight.

Notice in our workings above, the location of $a$ never really mattered. The logic for every node on the left side of the edge is the same. As a result, the total contribution of all right edges is simply sumRight * nodesLeft, where nodesLeft is the amount of nodes on the left side of the removed edge.

We can do the exact same process with the left edges, and we’re done!

Note: My original solution was written when I was planning to make this a query problem (Exact same problem, but rather than a single removed edge, we can think of $10^5$ possible removed edges, and what each of these removals would do for the graph), so my solution is over-engineered and hard to understand. I’ve also included team des approach which does what we outline above in a much simpler manner with a tree search centered at the removed edge, rather than fixing the tree structure at an arbitrary node, like my solution does.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+
N = int(input())
+from collections import defaultdict
+edges = []
+for i in range(N-1):
+    edges.append(list(map(int,input().split())))
+
+x = int(input())
+
+dedge = edges[x-1]
+
+G = defaultdict(list)
+
+for e in edges:
+    if e[0] != dedge[0] or e[1] != dedge[1]:
+        G[e[0]].append([e[1],e[2]])
+        G[e[1]].append([e[0],e[2]])
+
+
+
+def distance_from(node):
+    dist = {node: 0}
+    visited = {node: True}
+
+    def search(n):
+        for e in G[n]:
+            if e[0] not in visited:
+                visited[e[0]] = True
+                dist[e[0]] = dist[n] + e[1]
+                search(e[0])
+    search(node)
+
+    return dist
+
+D1 = distance_from(dedge[0])
+D2 = distance_from(dedge[1])
+
+len1 = len(D1) # number of nodes
+len2 = len(D2)
+sum1 = sum(D1.values()) # sum of contributions
+sum2 = sum(D2.values())
+
+s = sum1*len2 + sum2*len1 + dedge[2]*len1*len2
+
+print(s)
+
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+
n = int(input())
+
+adj_list = [[] for _ in range(n)]
+par = [None]*n
+
+roads = []
+for i in range(n-1):
+    i, j, d = list(map(int, input().split()))
+    adj_list[i-1].append((j-1, d))
+    adj_list[j-1].append((i-1, d))
+    roads.append((i-1, j-1, d))
+
+def dfs(root):
+    for child, distance in adj_list[root]:
+        if child == par[root]: continue
+        par[child] = root
+        dfs(child)
+
+dfs(0)
+
+_num_paths_below = [None]*n
+_num_paths_above = [None]*n
+_sum_paths_below = [None]*n
+_sum_paths_above = [None]*n
+
+def num_paths_below(i):
+    if _num_paths_below[i] != None:
+        return _num_paths_below[i]
+    cur = 1
+    for j, distance in adj_list[i]:
+        if par[i] == j: continue
+        cur += num_paths_below(j)
+    _num_paths_below[i] = cur
+    return cur
+
+def sum_paths_below(i):
+    if _sum_paths_below[i] != None:
+        return _sum_paths_below[i]
+    cur = 0
+    for j, distance in adj_list[i]:
+        if par[i] == j: continue
+        cur += sum_paths_below(j) + distance * num_paths_below(j)
+    _sum_paths_below[i] = cur
+    return cur
+
+def num_paths_above(i):
+    if _num_paths_above[i] != None:
+        return _num_paths_above[i]
+    if par[i] == None:
+        return 1
+    cur = num_paths_above(par[i]) + 1
+    for child, distance in adj_list[par[i]]:
+        if child == i: continue
+        if child == par[par[i]]: continue
+        cur += num_paths_below(child)
+    _num_paths_above[i] = cur
+    return cur
+
+def sum_paths_above(i):
+    if _sum_paths_above[i] != None:
+        return _sum_paths_above[i]
+    if par[i] == None:
+        return 0
+    for child, distance in adj_list[par[i]]:
+        if child == i: par_dist = distance
+    cur = sum_paths_above(par[i]) + par_dist * num_paths_above(par[i])
+    for child, distance in adj_list[par[i]]:
+        if child == i: continue
+        if child == par[par[i]]: continue
+        cur += sum_paths_below(child) + (par_dist + distance) * num_paths_below(child)
+    _sum_paths_above[i] = cur
+    return cur
+
+road_index = int(input())
+
+rx, ry, road_distance = roads[road_index - 1]
+if par[rx] == ry: rx, ry = ry, rx
+# Now, par[ry] = rx.
+
+sum_x_size = sum_paths_above(rx)
+for child, distance in adj_list[rx]:
+    if child == ry: continue
+    if child == par[rx]: continue
+    sum_x_size += sum_paths_below(child) + distance * num_paths_below(child)
+
+sum_y_size = sum_paths_below(ry)
+
+num_x_size = num_paths_above(rx)
+for child, distance in adj_list[rx]:
+    if child == ry: continue
+    if child == par[rx]: continue
+    num_x_size += num_paths_below(child)
+
+num_y_size = num_paths_below(ry)
+
+total_productivity_lost = sum_x_size * num_y_size + sum_y_size * num_x_size + road_distance * num_x_size * num_y_size
+print(total_productivity_lost)
+

Misc 3

Hint 1

This problem is kind of an either you get it or you don’t problem, so its hard to give hints.

Your first course of action should be deciding on an encoding for all positions in the game. You’ll need to keep track of the current position, as well as what pieces of the di-force you’ve collected. This is important as it determines where you need to go, which affects the expectation.

Hint 2

Try writing out a recursive formula for the expectation of the game ending at any particular state in the game.

For the end state (boss position, all of the di-force collected), the expectation is $0$. For all positions next to the boss, the state with all of the di-force collected will look like $\mathbb{E}(P) = 1 + \frac{1}{4} \times 0 + \frac{1}{4} \mathbb{E}(X) + \frac{1}{4} \mathbb{E}(Y) + \frac{1}{4} \mathbb{E}(Z)$, where $X, Y$ and $Z$ are possible positions one could move to (They could also be $P$!)

Solution

Take all possible states of the game, and we’ll make a recursive formula for the expected number of steps to end the game from that position.

If we do this, we’ll end up with a system of equations, with $N$ equations and $N$ unknowns. You can take it for granted that this board produces an actual expected value for all valid locations. So we can solve this using gaussian elimination to figure out the solution to all variables simultaneously!

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+
import sys
+
+n, m = list(map(int, input().split()))
+
+lines = [list(input()) for _ in range(n)]
+
+n_objectives = 2
+
+for x in range(n):
+    for y in range(m):
+        if lines[x][y] == "S":
+            spawn = (x, y)
+        elif lines[x][y] == "A":
+            lines[x][y] = 1
+        elif lines[x][y] == "B":
+            lines[x][y] = 1<<1
+        elif lines[x][y] == "G":
+            goal = (x, y)
+
+def node_encode(x, y, cur_state):
+    return m*x + y + cur_state * n * m
+
+def node_decode(pos):
+    cur_state = pos // (n * m)
+    if cur_state == 1<<n_objectives:
+        return "constant"
+    pos %= n * m
+    x = pos // m
+    pos %= m
+    y = pos
+    return x, y, cur_state
+
+matrix = [
+    [
+        0 for _ in range(n*m*(1<<n_objectives) + 1)
+    ] for _ in range(n*m*(1<<n_objectives))
+]
+
+for x in range(n):
+    for y in range(m):
+        if lines[x][y] == "X":
+            continue
+        for cur_state in range(1<<n_objectives):
+            # From this, we can move U/D/L/R
+            this_node = node_encode(x, y, cur_state)
+            if (x, y) == goal and cur_state == (1<<n_objectives)-1:
+                # We are done!
+                matrix[this_node][this_node] = 1
+                continue
+            options = []
+            for a, b in [
+                (x+1, y),
+                (x-1, y),
+                (x, y+1),
+                (x, y-1),
+            ]:
+                new_state = cur_state
+                if not (0 <= a < n and 0 <= b < m):
+                    a, b = x, y
+                if lines[a][b] == "X":
+                    a, b = x, y
+                if type(lines[a][b]) == int:
+                    new_state |= lines[a][b]
+                options.append(node_encode(a, b, new_state))
+            # negate so the postive values are equal to this.
+            matrix[this_node][this_node] = -1
+            # 1 more step
+            matrix[this_node][-1] = 1
+            for option in options:
+                matrix[this_node][option] += 1/4
+
+def reduced_row_echelon_form(matrix):
+    rowCount = len(matrix)
+    colCount = len(matrix[0])
+    lead = 0
+    for r in range(rowCount):
+        if colCount <= lead: return
+        i = r
+        while matrix[i][lead] == 0:
+            i += 1
+            if rowCount == i:
+                i = r
+                lead = lead + 1
+                if colCount == lead:
+                    return
+        # swap rows i and r
+        matrix[i], matrix[r] = matrix[r], matrix[i]
+        if matrix[r][lead] != 0:
+            div = matrix[r][lead]
+            for c in range(colCount):
+                matrix[r][c] /= div
+        for i in range(rowCount):
+            if i != r:
+                deduction = matrix[i][lead]
+                for c in range(colCount):
+                    matrix[i][c] -= deduction * matrix[r][c]
+        lead += 1
+
+reduced_row_echelon_form(matrix)
+
+start_node = node_encode(*spawn, 0)
+# Find the row with col value at start position equal to 1.
+for r in range(len(matrix)):
+    if matrix[r][start_node] != 0:
+        print("non-zero row", file=sys.stderr)
+        for idx, val in enumerate(matrix[r]):
+            if val != 0:
+                print(node_decode(idx), val, file=sys.stderr)
+
+        print(-matrix[r][-1])
+

Recursion 0

Hint 1

This problem is purely about implementation - there’s no tricks, you just need to simulate the sequence.

Make sure you are performing the MOD operation!

Hint 2

If you’re struggling with the implementation - search up a solution which computes the fibonacci numbers, and try translate it to this sequence.

Solution

As the hints say, this is purely an implementation problem. Because our recurrence looks back two steps, we need two temporary variables to store the current values in the sequence.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
MOD = int(1e9+7)
+
+g1 = 1 # After i iterations, g1 = g(i+1)
+g0 = 0 # After i iterations, g0 = g(i)
+
+n = int(input())
+for _ in range(n):
+    # set j = i + 1
+    # g(j+1) = 3*g(i+1) + g(i)
+    # g(j) = g(i+1)
+    g1, g0 = (3*g1 + g0) % MOD, g1
+
+print(g0)
+

Recursion 1

Hint 1

This is a rather famous problem - there is a well known formula for $F(2n)$ and $F(2n+1)$

Hint 2

If we repeatedly apply the rules for $F(2n)$ and $F(2n+1)$, We’ll have about $\log_2(n)$ unique values of $F$ we’ll need to compute. If we don’t cache our results though, you’ll run into issues.

Solution

Note: for this problem we have F(0) = 1, but usually, F(0) = 0. So for this solution, assume F(0) = 0, then we can simply output F(n+1) in our solution rather than F(n).

As the hints hint at, there is a well known formula for $F(2n)$ and $F(2n+1)$, rather than giving it to you, let’s prove it!

This proof makes use of a very unique way to generate fibonacci numbers - with matricies!

Notice that the following matrix:

\[M = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}\]

Paired with the following initial vector:

\[V = \begin{bmatrix} 1\\ 0 \end{bmatrix}\]

Can be used to generate fibonacci numbers! Let’s find out what happens when we repeatedy multiply $V$ by $M$

\[MV = \begin{bmatrix} 1\\ 1 \end{bmatrix}. M^2V = \begin{bmatrix} 2\\ 1 \end{bmatrix}. M^3V = \begin{bmatrix} 3\\ 2 \end{bmatrix}. M^4V = \begin{bmatrix} 5\\ 3 \end{bmatrix}\]

Spotting the pattern? In general, if we have

\[V = \begin{bmatrix} F(n)\\ F(n-1) \end{bmatrix}. MV = \begin{bmatrix} F(n) + F(n-1)\\ F(n) \end{bmatrix} = \begin{bmatrix} F(n+1)\\ F(n) \end{bmatrix}\]

The matrix $M$ moves each of the values in the vector along one step in the fibonacci sequence! This is because the top row of $M$ adds the two vector values together, and the bottom row of $M$ just preserves the top value of the vector.

Repeatedly multiplying matrix $M$ you’ll find that

\[M^n = \begin{bmatrix} F(n+1) & F(n)\\ F(n) & F(n-1) \end{bmatrix}\]

And so if

\[M^{2n}\begin{bmatrix} 1\\ 0 \end{bmatrix} = \begin{bmatrix} F(2n+1)\\ F(2n) \end{bmatrix}\]

Then since

\[M^{2n} = M^n \times M^n = \begin{bmatrix} F(n+1) & F(n)\\ F(n) & F(n-1) \end{bmatrix} \times \begin{bmatrix} F(n+1) & F(n)\\ F(n) & F(n-1) \end{bmatrix} = \begin{bmatrix} F(n+1)^2 + F(n)^2 & F(n+1)F(n) + F(n)F(n-1)\\ F(n+1)F(n) F(n)F(n-1) & F(n)^2 + F(n-1)^2 \end{bmatrix}\]

we have (after multiplying this by $V$):

\[F(2n+1) = F(n+1)^2 + F(n)^2, F(2n) = F(n+1)F(n) + F(n)F(n-1)\]

Using this rule, we can solve the problem in logarithmic time, provided you use dynamic programming.

That being said, since we have a matrix that generates the fibonacci sequence, we can just use fast matrix exponentation to find our result too! No need for the fancy formula.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
from functools import cache
+
+MOD = int(1e9+7)
+
+@cache
+def fibonacci(n):
+    if n <= 3:
+        return [0, 1, 1, 2][n]
+    m = n // 2
+    if n%2 == 0:
+        return (fibonacci(m+1)*fibonacci(m) + fibonacci(m)*fibonacci(m-1)) % MOD
+    return (fibonacci(m+1)*fibonacci(m+1) + fibonacci(m)*fibonacci(m)) % MOD
+
+print(fibonacci(int(input()) + 1))
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+
import math
+
+MOD = int(1e9+7)
+
+matrix = [
+    [1, 1],
+    [1, 0]
+]
+
+def mat_mult(m1, m2):
+    result = [[0 for _ in range(len(m2[0]))] for _ in range(len(m1))]
+    for x in range(len(m1)):
+        for y in range(len(m2[0])):
+            res = 0
+            for a in range(len(m2)):
+                res += m1[x][a] * m2[a][y]
+                res = res % MOD
+            result[x][y] = res
+    return result
+
+def exponentiate(mat, p):
+    cur_val = [[int(i1==i2) for i2 in range(len(mat[0]))] for i1 in range(len(mat))]
+    cur_power = mat
+    for shift in range(2+math.floor(math.log2(p))):
+        if (1 << shift) & p:
+            cur_val = mat_mult(cur_val, cur_power)
+        cur_power = mat_mult(cur_power, cur_power)
+    return cur_val
+
+n = int(input()) + 1
+move = exponentiate(matrix, n)
+moved = mat_mult(move, [[1], [0]])
+print(int(moved[1][0]))
+

Recursion 2

Hint 1

If you haven’t already, give the solution to Recursion 1 a look, even if you solved the problem. Some of the tools there might be useful.

Hint 2

You need to use the matrix solution from recursion 1, however our new matrix needs to compute $G$ rather than $F$. How can we do this?

Solution

As the hints say, we need to come up with a matrix $M$ that when multiplied by a vector $V$, moves it through the $G$ sequence.

The trick here is to notice that while a $2 \times 2$ matrix makes this impossible, we can achieve this if we have a $4\times 4$ matrix, by keeping two rows for tracking the fibonacci sequence and two rows for tracking $G$:

\[M = \begin{bmatrix} 1 & 1 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 1 & 0 & 2 & 3 \\ 0 & 0 & 1 & 0 \end{bmatrix}\]

Notice how the top left corner is the same matrix that generates $F$. So if the first two rows of $V$ are the same, then the first two rows of $V$ continue to generate the fibonacci numbers.

The bottom row of $M$ does the same thing as the second row did in the original - It keeps the bottom row one iteration before the third row.

The only remaining row - the third one - does the actual calcuation. $G(n) = 2 \times G(n-1) + 3 \times G(n-2) + F(n-1)$. Notice that since $G(n)$ is evaluated in the matrix the same time as $F(n)$, adding the top row from the previous iteration constitutes $F(n-1)$.

Multiplying this with \(V = \begin{bmatrix}F(1)\\ F(0)\\ G(1)\\ G(0)\end{bmatrix}\) advances the vector into \(V = \begin{bmatrix}F(2)\\ F(1)\\ G(2)\\ G(1)\end{bmatrix}\)

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+
import math
+
+MOD = int(1e9+7)
+
+matrix = [
+    [1, 1, 0, 0],
+    [1, 0, 0, 0],
+    [1, 0, 2, 3],
+    [0, 0, 1, 0]
+]
+
+def mat_mult(m1, m2):
+    result = [[0 for _ in range(len(m2[0]))] for _ in range(len(m1))]
+    for x in range(len(m1)):
+        for y in range(len(m2[0])):
+            res = 0
+            for a in range(len(m2)):
+                res += m1[x][a] * m2[a][y]
+                res = res % MOD
+            result[x][y] = res
+    return result
+
+def exponentiate(mat, p):
+    cur_val = [[int(i1==i2) for i2 in range(len(mat[0]))] for i1 in range(len(mat))]
+    cur_power = mat
+    for shift in range(2+math.floor(math.log2(p))):
+        if (1 << shift) & p:
+            cur_val = mat_mult(cur_val, cur_power)
+        cur_power = mat_mult(cur_power, cur_power)
+    return cur_val
+
+n = int(input())
+if n == 0:
+    print(0)
+else:
+    move = exponentiate(matrix, n)
+    moved = mat_mult(move, [[1], [1], [5], [0]])
+    # 2 1 11 5
+    # 3 2 39 11
+    print(int(moved[3][0]))
+

Maybe there’s a special kind of rule here too, but I haven’t gone looking for one.

Recursion 3

Hint 1

If you’ve solved recursion 2, this is just a harder version of that. Try come up with a matrix that computes the sequence. You may need multiple intermediate sequences.

Hint 2

The particular sequence $B(n) = B(n-1) + A(n), B(0) = A(0)$ may be useful. What is $B(n)$ in closed form?

What about the sequence $C(n) = C(n-1) + B(n), C(0) = B(0)$?

Solution

Note: I’m again going to assume $F(0)=0$, then we can simply translate $H(n) = 4\times H(n-1) + \sum_{i=1}^{n-1} ((n-i)^2\times F(i-1))$ into $H(n) = 4\times H(n-1) + \sum_{i=0}^{n-1} ((n-i)^2\times F(i))$, since $n \times 0 = 0$.

Let’s first tackle some of the questions in Hint 2, on our quest to find a matrix.

$B(n) = \sum_{i=0}^{n} A(i)$, and $C(n) = \sum_{i=0}^n B(i) = \sum_{i=0}^n \sum_{j=0}^i A(j) = \sum_{i=0}^n (n + 1 -i)A(i)$

Interesting… What about $D(n) = D(n-1) + C(n)$?

This would have closed form $D(n) = \sum_{i=0}^n C(i) = \sum_{i=0}^n \sum_{j=0}^i (i-j+1)A(j) = \sum_{i=0}^n \frac{(n-i+1)(n-i+2)}{2} A(i) = \sum_{i=0}^n \frac{1}{2}((n-i)^2 + 3(n-i) + 2) A(i)$.

Very interesting…

What if we took $2D(n) - 3C(n) + B(n)$? That would give us $\sum_{i=0}^n ((n-i)^2 + 3(n-i) + 2 - 3(n-i) - 3 + 1) A(i) = \sum_{i=0}^n (n-i)^2 A(i)$. That’s pretty much it! Just the top boundary of the sum is wrong.

What if we instead defined $B(n) = A(n-1) + B(n-1), B(0) = 0$? Then we’d have $B(n) = \sum_{i=0}^{n-1} A(i)$.

Keeping the definition of $C$, we’d have $C(n) = \sum_{i=0}^n B(i) = \sum_{i=0}^n \sum_{j=0}^{i-1} A(j) = \sum_{i=0}^{n-1} (n-i)A(i)$.

Keeping the definition of $D$, we’d have $D(n) = \sum_{i=0}^n C(i) = \sum_{i=0}^n \sum_{j=0}^{i-1} (n-j)A(j) = \sum_{i=0}^{n-1} \frac{(n-i)(n-i+1)}{2}A(i) = \sum_{i=0}^{n-1} \frac{1}{2}((n-i)^2 + (n-i))A(i)$

Using this new definition, what is $2D(n) - C(n)$?

\[\sum_{i=0}^{n-1} ((n-i)^2 + (n-i) - (n-i))A(i) = \sum_{i=0}^{n-1} (n-i)^2A(i)\]

Bingo!

Using a similar strategy to before, we define our matrix:

\[\begin{bmatrix} 1 & 1 & 0 & 0 & 0 & 0 & 0\\ 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 1 & 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & -1 & 2 & 4\\ \end{bmatrix}\]

Let’s analyse this row by row.

  • Rows 1 and 2 compute $F(n+2)$ and $F(n+1)$ respectively. You’ll see the reason for the offsets later.
  • Row 3 computes $B(n+2)$ (From now on, assume we’ve set $A = F$). This adds $F(n+2)$ to $B(n+2)$.
  • Row 4 computes $C(n+1)$. This adds $B(n+2)$ to $C(n+1)$.
  • Row 5 computes $C(n)$. This will be needed for later.
  • Row 6 computes $D(n)$. This adds $C(n+1)$ to $D(n)$.
  • Row 7 computes $H(n)$. This adds $4H(n)$ to $2D(n)$ and subtracts $C(n)$ from the result.

With all this in place, we need only define our vector $V$, to contain $F(2), F(1), B(2), C(1), C(0), D(0), H(0)$.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+
import math
+
+MOD = int(1e9+7)
+
+matrix = [
+    [1, 1, 0, 0, 0, 0, 0], # fn+2
+    [1, 0, 0, 0, 0, 0, 0], # fn+1
+    [1, 0, 1, 0, 0, 0, 0], # bn+2
+    [0, 0, 1, 1, 0, 0, 0], # cn+1
+    [0, 0, 0, 1, 0, 0, 0], # cn
+    [0, 0, 0, 1, 0, 1, 0], # dn
+    [0, 0, 0, 0, -1, 2, 4], #hn
+]
+
+def mat_mult(m1, m2):
+    result = [[0 for _ in range(len(m2[0]))] for _ in range(len(m1))]
+    for x in range(len(m1)):
+        for y in range(len(m2[0])):
+            res = 0
+            for a in range(len(m2)):
+                res += m1[x][a] * m2[a][y]
+                res = res % MOD
+            result[x][y] = res
+    return result
+
+def exponentiate(mat, p):
+    cur_val = [[int(i1==i2) for i2 in range(len(mat[0]))] for i1 in range(len(mat))]
+    cur_power = mat
+    for shift in range(2+math.floor(math.log2(p+1))):
+        if (1 << shift) & p:
+            cur_val = mat_mult(cur_val, cur_power)
+        cur_power = mat_mult(cur_power, cur_power)
+    return cur_val
+
+n = int(input())
+move = exponentiate(matrix, n)
+
+col = [[2], [1], [2], [1], [0], [0], [0]]
+
+moved = mat_mult(move, col)
+print(int(moved[6][0]))
+
+# 0, 1, 9, 51
+# 4*0 + 1 * F(1) = 1 OR 2*1-1
+# 4*1 + 4 * F(1) + 1 * F(2) = 9
+# 4*9 + 9 * F(1) + 4 * F(2) + 1 * F(3) = 36 + 9 + 4 + 2 = 51
+

Note that we can generate matricies using this trick for any upper bound on the sum, not just $n-1$, and any polynomial of $n-i$, not just $(n-i)^2$. I’d like to encourage you to think of other cool recursive sequences we can model via matrix multiplication.

This post is licensed under GNU GPL V3 by the author.
diff --git a/posts/factorization/index.html b/posts/factorization/index.html new file mode 100644 index 0000000..cdd671c --- /dev/null +++ b/posts/factorization/index.html @@ -0,0 +1,261 @@ + Primes and Factorization Techniques | Monash Code Binder
Posts Primes and Factorization Techniques
Post
Cancel

Primes and Factorization Techniques

Why?

Many number theoretic problems in competitive programming require analysing the factors or prime factors of a number. Here I’ll list a few techniques for finding these factors, and some techniques / properties involving the factors / prime factors of a number.

Preliminaries

First off, lets define our basic terms, then we can get into the interesting stuff.

Definitions

For a positive integer \(x\), a positive integer \(y\) is a factor of \(x\) if \(x\) can be expressed as \(yk\) for some other positive integer \(k\). A factor \(y\) is a prime factor if \(y\) is prime.

A factorization of a positive integer \(x\) is a collection of (not necessarily distinct) factors \(y_i\) satisfying \(y_0 \times y_1 \times \cdots \times y_k = x\). A prime factorization is a factorization where all \(y_i\) are prime factors.

All the below properties depend on the fact that 1 is not a prime number. I repeat: in all algorithms from now on (and I think you should adopt this generally too) we do not consider 1 to be a prime number!

Basic Properties

Every positive integer has a single unique prime factorization. I’ll leave it as an exercise for you to prove this is true with induction.

Let’s suppose \(x\) can be written in the form

\[x = y_0^{a_0} \times y_1^{a_1} \times y_2^{a_2} \times \cdots ,\]

where $y_i$ are distinct primes. Then there are \((a_0+1)\times(a_1+1)\times(a_2+1)\times\cdots\) factors of $x$, since any factor can use anywhere from \(0\) to \(a_i\) copies of prime \(p_i\).

Given the prime factorization of two numbers \(x\) and \(y\), we can find the gcd simply by considering the intersection of their factorizations. The lcm of those two numbers is the union of their factorizations.

Finding Factors / Primes

Sieve of Eratosthenes

A simple (but powerful) approach is that of the Erathosthenes: Go through each prime, and mark all multiples of that prime as not prime.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
def sieve(n):
+  isprime = [True]*(n+1)
+  isprime[0], isprime[1] = False, False
+  i=2
+  while i*i <= n:
+    if not isprime[i]:
+      i += 1
+      continue
+    j=i*i
+    while j <= n:
+      isprime[j] = False
+      j += i
+    i += 1
+  return isprime
+
1
+2
+3
+4
+5
+6
+7
+8
+
vector<bool> isprime;
+void sieve(int n) {
+  isprime.assign(n+1, 1);
+  isprime[0] = isprime[1] = 0;
+  for (int i=2; i * i <= n; i++) if (isprime[i]) {
+    for (int j=i*i; j <= n; j+= i) isprime[j] = 0;
+  }
+}
+

There is some optimization going on here. We only want to mark off multiples of \(i\) in the isprime vector where \(i\) is the smallest prime factor of that multiple. This is the reason we can end when \(i \times i > n\) and start at \(j = i \times i\).

This operation is contest safe until about \(10^8\).

Factors with Sieve

While finding whether a number is prime is all well and good, rather than just recording primality, why not also record what prime factor caused us not to be prime? From this information we can then generate prime factors of a number, if we wish.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
def factor_sieve(n):
+  # factor[i] = a factor of i. factor[1] = 0.
+  factor = [0]*(n+1)
+  primes = []
+  for i in range(2, n+1):
+    if factor[i] == 0:
+      factor[i] = i
+      primes.append(i)
+    # Make sure that the smallest factor is always listed.
+    for p in primes:
+      if (p > factor[i] or i * p > n): break
+      factor[i * p] = p
+  return factor, primes
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
vi factor, primes;
+void factor_sieve(int n) {
+  // factor[i] = a factor of i. factor[1] = 0.
+  factor.assign(n+1, 0);
+  for (int i=2; i <= n; i++) {
+    if (factor[i] == 0) {
+      factor[i] = i;
+      primes.push_back(i);
+    }
+    // Make sure that the smallest factor is always listed.
+    for (int p: primes) if (p > factor[i] || i * p > n) break; else factor[i * p] = p;
+  }
+}
+

This operation is contest safe until about \(10^7\).

Prime Factorizations with precomp

If we want to get a full list of prime factors given we’ve done the above precomputation, we can just repeatedly divide i by factor[i] until we hit 1.

1
+2
+3
+4
+5
+6
+7
+8
+9
+
def fast_factors(n, factor):
+  res = []
+  while n > 1:
+    f = factor[n]
+    # Remove this line to include duplicate factors.
+    while n % f == 0:
+      n //= f
+    res.append(f)
+  return res
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+
vi fast_factors(int n) {
+  vi res;
+  while (n > 1) {
+    int f = factor[n];
+    while (n % f == 0) n /= f; // Remove the while to include duplicate factors.
+    res.push_back(f);
+  }
+  return res;
+}
+

Prime factors without precomp

If you are only testing a few numbers for prime factors, you can instead do this in \(O(\sqrt{n})\):

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
def slow_factors(n):
+  res = []
+  i = 2
+  while i*i <= n:
+    # Change to while for duplicates
+    if n % i == 0:
+      res.append(i)
+      # Remove while for duplicates.
+      while n % i == 0:
+        n //= i
+    i += 1
+  if n > 1:
+    res.append(n)
+  return res
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
vll slow_factors(ll n) {
+  vll res;
+  for (ll i=2; i*i <= n; i++)
+    if (n % i == 0) { // change to while for duplicates
+      res.push_back(i);
+      while (n % i == 0) n /= i; // Remove while for duplicates
+    }
+  if (n > 1) res.push_back(n);
+  return res;
+}
+

Fast Prime Testing

The first three algorithms are good when you need to know the primality / factors of numbers in a range. If you just want to test a single number, we can do much better though. As we’ve seen we can get the prime factorization in \(O(\sqrt{n})\), but for just primality testing, we can do \(O(\log(n))\) with Miller-Rabin.

I won’t go into details how this works, but we provide Miller-Rabin with some bases depending on how big our number is:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
val = [2, 7, 61]                                    # for n <= 2^32
+val = [2, 13, 23, 1662803]                          # for n <= 10^12
+val = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]  # for n <= 2^64
+
+def isprime(n):
+  if n < 2: return False
+  d = n - 1
+  s = 0
+  while d & 1 == 0:
+    d >>= 1
+    s += 1
+  for v in val:
+    if v >= n: break
+    # v^d mod n.
+    x = expmod(v, d, n)
+    if (x == 1 or x == n-1): continue
+    for r in range(s):
+      x = expmod(x, 2, n)
+      if (x == n - 1): break
+    else:
+      return False
+  return True
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
vi val = {2, 7, 61};                                    // n <= 2^32
+vi val = {2, 13, 23, 1662803};                          // n <= 10^12
+vi val = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};  // n <= 2^64 (replace ll with __int128)
+
+bool isprime(ll n) {
+  if (n < 2) return false;
+  ll s = __builtin_ctzll(n-1), d = (n - 1) >> s;
+  for (int v: val) {
+    if (v >= n) break;
+    ll x = expmod(v, d, n);
+    if (x == 1 || x == n-1) continue;
+    // big is typedef'd to __int128
+    for (ll r=1; r<s; r++) if ((x = ((big(x)*x) % n)) == n-1) goto nextPrime;
+    return false;
+    nextPrime:;
+  }
+  return true;
+}
+

You can learn more about Deterministic Miller-Rabin here.

Cool properties

Since factors and primes can be thought of as the building blocks of all the integers and to some extent are still shrouded in mystery, there are lots of cool properties of primes that we might want to utilise in programming problems.

Squares

The prime factorization of squares has \(a_i\) even for all \(i\), where

\[x = y_0^{a_0}\times y_1^{a_1} \times y_2^{a_2} \times \cdots.\]

Another way of thinking about this is that every square number can be factorized into prime squares.

In general for powers of 3, 4, etc. Simply replace “\(a_i\) even” with “\(a_i\) divisible by 3, 4, etc”.

Sums

Suppose we add two coprime numbers \(A\) and \(B\) (Numbers with gcd\((A, B) = 1\)). We know their sum must then be coprime to both \(A\) and \(B\) (\(\text{gcd}(A+B, A) = \text{gcd}(A+B, B) = 1\)). In other words, the prime factorization of \(A+B\) comprises of primes which are neither in \(A\) nor \(B\).

Factorization Wheels

While going through with the Sieve method is good for finding the composite / prime status of every number, if you just want to list off primes it might be better to combine with a wheel factorization method, allowing you to strike off many candidates based on the first few factors.

You can find out about wheel factorization here.

Sieve starting at different positions

As we’ve seen previously, the sieve of Eratosthenes only needs to consider primes less than \(\sqrt{n}\) for the algorithm to work (This can be reasoned by noting that any composite number requires a factor less than or equal to \(\sqrt{n}\)).

Therefore, we can modify our solution slightly to compute the prime numbers in some other range. For example, to compute the primes between \(10^{12} - 10^7\) and \(10^{12}\):

  • Use the sieve to find all primes less than \(\sqrt{10^{12}} = 10^6\).
  • Sieve over these primes in the range \(10^{12} - 10^7\) to \(10^{12}\). Anything not marked as composite must be prime!

We can actually continue this argument with contiguous segments to get a \(\sqrt{n}\) space complexity sieve implementation. For details see here

This post is licensed under GNU GPL V3 by the author.
diff --git a/posts/index.html b/posts/index.html new file mode 100644 index 0000000..c31a99c --- /dev/null +++ b/posts/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/posts/lca/index.html b/posts/lca/index.html new file mode 100644 index 0000000..93dd78d --- /dev/null +++ b/posts/lca/index.html @@ -0,0 +1,1083 @@ + Least Common Ancestor (LCA) | Monash Code Binder
Posts Least Common Ancestor (LCA)
Post
Cancel

Least Common Ancestor (LCA)

Where is this useful?

The Least Common Ancestor (LCA) data structure is useful wherever you have a directed graph where every vertex has out-degree \(\leq 1\). In more common terms, each vertex has a unique determined ‘parent’, or it is a root node, with no parent. The most common (and almost always only) example being a rooted tree.

On these particular graphs, the LCA gives us a fast way to move ‘up’ the graph (Towards your parents). In particular, we can use this to find the least common ancestor in \(\log (N)\) time, where the data structure gets its name from.

Reusing the analogy of parenting vertices, a vertex \(u\) is an ancestor of \(v\) if \(u\) is \(v\)’s parent, or \(v\)’s parent’s parent, and so on. As long as there is a line of ‘parentage’ connecting \(v\) to \(u\), \(u\) is an ancestor of \(v\). We consider \(v\) to also be it’s own ancestor.

The least common ancestor problem then requires, given two vertices \(x\) and \(y\), to find a vertex \(z\) in the graph such that \(z\) is an ancestor of both \(x\) and \(y\), but there is no vertex \(z’ \neq z\) such that \(z\) is an ancestor of \(z’\) and \(z’\) is an ancestor of \(x\) and \(y\) (In the tree example, we just want to find the lowest depth vertex whose subtree contains boths \(x\) and \(y\)).

Note that the least common ancestor can be \(x\) or \(y\), if \(x\) is an ancestor of \(y\) or vice-versa.

Implementing the Data Structure

Interface

Let’s start by defining an interface for this data structure, and then slowly implement our methods.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
class LCA:
+"""
+vertices are represented as numbers 0->n-1.
+"""
+
+    def __init__(self, n_vertices):
+        self.n = n_vertices
+        self.adjacent = [[] for _ in range(self.n)]
+
+    def add_edge(self, u, v, weight=1):
+        self.adjacent[u].append((v, weight))
+        self.adjacent[v].append((u, weight))
+
+    def build(self, root):
+        # Once edges are added, build the tree/data structure.
+        pass # TODO
+
+    def query(self, u, v, root=None):
+        # What is the lowest common ancestor of u, v?
+        # Extension: Make this query from any root vertex you want.
+        pass # TODO
+
+    def dist(self, u, v):
+        # Find the distance between two vertices - very simple if we have LCA.
+        pass # TODO
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+
template<typename T = int> struct LCA {
+    // vertices are represented as numbers 0->n-1.
+    int n; vector<vector<pair<int, T> > > adjacent;
+
+    LCA(int n_vertices) : n(n_vertices), adjacent(n) { }
+
+    void add_edge(int u, int v, T weight=1) {
+        adjacent[u].emplace_back(v, weight);
+        adjacent[v].emplace_back(u, weight);
+    }
+
+    void build(int root=0) {
+        // Once edges are added, build the tree/data structure.
+        // TODO
+    }
+
+    int query(int u, int v, int root=-1) {
+        // What is the lowest common ancestor of u, v?
+        // Extension: Make this query from any root vertex you want.
+        // TODO
+    }
+
+    T dist(int u, int v) {
+        // Find the distance between two vertices - very simple if we have LCA.
+        // TODO
+    }
+}
+

Useful data

First off, let’s save some intermediary data that will make our life a lot easier, and strictly define the tree structure. We’ll introduce three arrays: parent, level and length.

  • parent stores the direct parent of any vertex in the rooted tree.
  • level stores the level of the tree the vertex is at (Number of edges from it to the root)
  • length stores the length of the vertex to the root (Using edge weights).

We’ll populate these fields in the build method, since all edges should be added by then.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+
class LCA:
+"""
+vertices are represented as numbers 0->n-1.
+"""
+
+    def __init__(self, n_vertices):
+        self.n = n_vertices
+        self.adjacent = [[] for _ in range(self.n)]
+
+    def add_edge(self, u, v, weight=1):
+        self.adjacent[u].append((v, weight))
+        self.adjacent[v].append((u, weight))
+
+    def dfs(self, source, c_parent, c_level, c_length):n
+        # Search from the source down the tree and set parent, level, length accordingly.n
+        self.parent[source] = c_parentn
+        self.level[source] = c_leveln
+        self.length[source] = c_lengthn
+        for child, weight in self.adjacent[source]:n
+            if child != c_parent:n
+                self.dfs(child, source, c_level + 1, c_length + weight)n
+
+    def build(self, root):
+        # Once edges are added, build the tree/data structure.
+        self.parent = [None]*self.nn
+        self.level = [None]*self.nn
+        self.length = [None]*self.nn
+        self.dfs(root, -1, 0, 0)n
+
+    def query(self, u, v, root=None):
+        # What is the lowest common ancestor of u, v?
+        # Extension: Make this query from any root vertex you want.
+        pass # TODO
+
+    def dist(self, u, v):
+        # Find the distance between two vertices - very simple if we have LCA.
+        pass # TODO
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+
template<typename T = int> struct LCA {
+    // vertices are represented as numbers 0->n-1.
+    int n; vector<vector<pair<int, T> > > adjacent;
+    vi parent, level;n
+    vector<T> length;n
+
+    LCA(int n_vertices) : n(n_vertices), adjacent(n), parent(n), level(n), length(n) { }m
+
+    void add_edge(int u, int v, T weight=1) {
+        adjacent[u].emplace_back(v, weight);
+        adjacent[v].emplace_back(u, weight);
+    }
+
+    void dfs(int source, int c_parent, int c_level, T c_length) {n
+        // Search from the source down the tree and set parent, level, length accordingly.n
+        parent[source] = c_parent;n
+        level[source] = c_level;n
+        length[source] = c_length;n
+        for (auto v: adjacent[source])n
+            if (v.first != c_parent)n
+                dfs(v.first, source, c_level+1, c_length+v.second);n
+    }
+
+    void build(int root=0) {
+        // Once edges are added, build the tree/data structure.
+        dfs(root, -1, 0, 0);n
+    }
+
+    int query(int u, int v, int root=-1) {
+        // What is the lowest common ancestor of u, v?
+        // Extension: Make this query from any root vertex you want.
+        // TODO
+    }
+
+    T dist(int u, int v) {
+        // Find the distance between two vertices - very simple if we have LCA.
+        // TODO
+    }
+}
+

So now we can query many useful characteristics of vertices in rooted trees. Now for the interesting part: let’s start creating data unique to the LCA structure.

Ancestor Array

LCA gets its fast queries by precomputing a special array, called ancestor. Ancestor is a 2 dimensional array with ancestor[v][k] storing the ancestor of vertex v \(2^k\) edges towards the root. As an example, ancestor[v][0] is parent[v] (Parent is just ancestor 1 edge towards the root), and ancestor[v][1] is parent[parent[v]] where appropriate (2 edges towards root is same as parent’s parent).

If you just populated this array by searching up the tree \(2^k\) steps each time, you’d have worst case complexity \(O(n^2)\) to build the array. Luckily, we can use the fact that ancestor[v][k] = ancestor[ancestor[v][k-1]][k-1] (In other words, you can move \(2^k\) steps towards the root by first moving \(2^{k-1}\) steps, which we’ve already computed, and then another \(2^{k-1}\) steps from this new position). This reduces the complexity to \(O(n\log_2(n))\)

We do this so that we can find the ancestor \(m\) edges towards the root for any arbitrary \(m\) in \(\log_2(m)\) time, while only using \(\log_2(n)\) space. We’ll see how this gets done later.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+
class LCA:
+"""
+vertices are represented as numbers 0->n-1.
+"""
+
+    # number such that 2^{MAX_LOG} > n. 20 works for n <= 10^6.n
+    MAX_LOG = 20n
+
+    def __init__(self, n_vertices):
+        self.n = n_vertices
+        self.adjacent = [[] for _ in range(self.n)]
+
+    def add_edge(self, u, v, weight=1):
+        self.adjacent[u].append((v, weight))
+        self.adjacent[v].append((u, weight))
+
+    def dfs(self, source, c_parent, c_level, c_length):
+        # Search from the source down the tree and set parent, level, length accordingly.
+        self.parent[source] = c_parent
+        self.level[source] = c_level
+        self.length[source] = c_length
+        for child, weight in self.adjacent[source]:
+            if child != c_parent:
+                self.dfs(child, source, c_level + 1, c_length + weight)
+
+    def build(self, root):
+        # Once edges are added, build the tree/data structure.
+        self.parent = [None]*self.n
+        self.level = [None]*self.n
+        self.length = [None]*self.n
+        self.dfs(root, -1, 0, 0)
+        # Compute ancestorn
+        self.ancestor = [[-1]*self.MAX_LOG for _ in range(self.n)]n
+        # Initial step: ancestor[v][0] = parent[v]n
+        for v in range(self.n):n
+            self.ancestor[v][0] = self.parent[v]n
+        # Now, compute ancestor[v][k] from 1->MAX_LOGn
+        for k in range(1, self.MAX_LOG):n
+            for v in range(self.n):n
+                if self.ancestor[v][k-1] != -1:n
+                    # Move 2^{k-1} up, then 2^{k-1} again.n
+                    self.ancestor[v][k] = self.ancestor[self.ancestor[v][k-1]][k-1]n
+
+    def query(self, u, v, root=None):
+        # What is the lowest common ancestor of u, v?
+        # Extension: Make this query from any root vertex you want.
+        pass # TODO
+
+    def dist(self, u, v):
+        # Find the distance between two vertices - very simple if we have LCA.
+        pass # TODO
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+
template<typename T = int> struct LCA {
+    // vertices are represented as numbers 0->n-1.
+    // number such that 2^{MAX_LOG} > n. 20 works for n <= 10^6.n
+    int MAX_LOG = 20;n
+    int n; vector<vector<pair<int, T> > > adjacent;
+    vi parent, level;
+    vvi ancestor;n
+    vector<T> length;
+
+    LCA(int n_vertices) : n(n_vertices), adjacent(n), parent(n), level(n), length(n) { }
+
+    void add_edge(int u, int v, T weight=1) {
+        adjacent[u].emplace_back(v, weight);
+        adjacent[v].emplace_back(u, weight);
+    }
+
+    void dfs(int source, int c_parent, int c_level, T c_length) {
+        // Search from the source down the tree and set parent, level, length accordingly.
+        parent[source] = c_parent;
+        level[source] = c_level;
+        length[source] = c_length;
+        for (auto v: adjacent[source])
+            if (v.first != c_parent)
+                dfs(v.first, source, c_level+1, c_length+v.second);
+    }
+
+    void build(int root=0) {
+        // Once edges are added, build the tree/data structure.
+        dfs(root, -1, 0, 0);
+        // Compute ancestorn
+        ancestor.assign(n, vi(MAX_LOG, -1));n
+        // Initial step: ancestor[v][0] = parent[v]n
+        for (int v=0; v<n; v++)n
+            ancestor[v][0] = parent[v];n
+        // Now, compute ancestor[v][k] from 1->MAX_LOGn
+        for (int k=1; k < MAX_LOG; k++)n
+            for (int v=0; v<n; v++)n
+                if (ancestor[v][k-1] != -1) {n
+                    // Move 2^{k-1} up, then 2^{k-1} again.n
+                    ancestor[v][k] = ancestor[ancestor[v][k-1]][k-1];n
+                }n
+    }
+
+    int query(int u, int v, int root=-1) {
+        // What is the lowest common ancestor of u, v?
+        // Extension: Make this query from any root vertex you want.
+        // TODO
+    }
+
+    T dist(int u, int v) {
+        // Find the distance between two vertices - very simple if we have LCA.
+        // TODO
+    }
+}
+

Query

That’s actually most of the ingenuity out of the way, now we can get to implementing query.

Provided we want the LCA with respect to the root we called build from, we can define the LCA l of u and v in the following way:

l is the ancestor of u and v maximising level[l].

We also know that level[l] <= min(level[u], level[v]). Using this, we can calculate query(u, v) by:

  • Finding the ancestors of u and v (call them a1, a2) such that level[a1] = level[a2] = min(level[u], level[v]).
  • Keep moving a1 and a2 towards the root (higher and higher ancestors) until a1 == a2. Then a1 and a2 are the LCA of u and v.

We can do both of these things on \(\log_2(n)\) time with this ancestor array we’ve generated. Let’s see how:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+
class LCA:
+"""
+vertices are represented as numbers 0->n-1.
+"""
+
+    # number such that 2^{MAX_LOG} > n. 20 works for n <= 10^6.
+    MAX_LOG = 20
+
+    def __init__(self, n_vertices):
+        self.n = n_vertices
+        self.adjacent = [[] for _ in range(self.n)]
+
+    def add_edge(self, u, v, weight=1):
+        self.adjacent[u].append((v, weight))
+        self.adjacent[v].append((u, weight))
+
+    def dfs(self, source, c_parent, c_level, c_length):
+        # Search from the source down the tree and set parent, level, length accordingly.
+        self.parent[source] = c_parent
+        self.level[source] = c_level
+        self.length[source] = c_length
+        for child, weight in self.adjacent[source]:
+            if child != c_parent:
+                self.dfs(child, source, c_level + 1, c_length + weight)
+
+    def build(self, root):
+        # Once edges are added, build the tree/data structure.
+        self.parent = [None]*self.n
+        self.level = [None]*self.n
+        self.length = [None]*self.n
+        self.dfs(root, -1, 0, 0)
+        self.ancestor = [[-1]*self.MAX_LOG for _ in range(self.n)]
+        # Initial step: ancestor[v][0] = parent[v]
+        for v in range(self.n):
+            self.ancestor[v][0] = self.parent[v]
+        # Now, compute ancestor[v][k] from 1->MAX_LOG
+        for k in range(1, self.MAX_LOG):
+            for v in range(self.n):
+                if self.ancestor[v][k-1] != -1:
+                    # Move 2^{k-1} up, then 2^{k-1} again.
+                    self.ancestor[v][k] = self.ancestor[self.ancestor[v][k-1]][k-1]
+
+    def query(self, u, v, root=None):
+        # What is the lowest common ancestor of u, v?
+        # Extension: Make this query from any root vertex you want.
+
+        if root is not None:n
+            pass # TODOn
+        # assume that u is higher up than v, to simplify the code belown
+        if self.level[u] > self.level[v]:n
+            u, v = v, un
+        # STEP 1: set u and v to be ancestors with the same leveln
+        for k in range(self.MAX_LOG-1, -1, -1):n
+            if (self.level[v] - (1 << k) >= self.level[u]):n
+                # If v is 2^k levels below u, move it up 2^k levels.n
+                v = self.ancestor[v][k]n
+        # We can be certain that level[u] = level[v]. Reason: binary representation of all natural numbers.n
+        # Do we need to move to step 2?n
+        if (u == v): return un
+        # STEP 2: find the highest ancestor where u != v. Then the parent is the LCAn
+        for k in range(self.MAX_LOG-1, -1, -1):n
+            if (self.ancestor[u][k] != self.ancestor[v][k]):n
+                # Move up 2^k stepsn
+                u = self.ancestor[u][k]n
+                v = self.ancestor[v][k]n
+        return self.parent[u]n
+
+    def dist(self, u, v):
+        # Find the distance between two vertices - very simple if we have LCA.
+        pass # TODO
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+
template<typename T = int> struct LCA {
+    // vertices are represented as numbers 0->n-1.
+    // number such that 2^{MAX_LOG} > n. 20 works for n <= 10^6.
+    int MAX_LOG = 20;
+    int n; vector<vector<pair<int, T> > > adjacent;
+    vi parent, level;
+    vvi ancestor;
+    vector<T> length;
+
+    LCA(int n_vertices) : n(n_vertices), adjacent(n), parent(n), level(n), length(n) { }
+
+    void add_edge(int u, int v, T weight=1) {
+        adjacent[u].emplace_back(v, weight);
+        adjacent[v].emplace_back(u, weight);
+    }
+
+    void dfs(int source, int c_parent, int c_level, T c_length) {
+        // Search from the source down the tree and set parent, level, length accordingly.
+        parent[source] = c_parent;
+        level[source] = c_level;
+        length[source] = c_length;
+        for (auto v: adjacent[source])
+            if (v.first != c_parent)
+                dfs(v.first, source, c_level+1, c_length+v.second);
+    }
+
+    void build(int root=0) {
+        // Once edges are added, build the tree/data structure.
+        dfs(root, -1, 0, 0);
+        // Compute ancestor
+        ancestor.assign(n, vi(MAX_LOG, -1));
+        // Initial step: ancestor[v][0] = parent[v]
+        for (int v=0; v<n; v++)
+            ancestor[v][0] = parent[v];
+        // Now, compute ancestor[v][k] from 1->MAX_LOG
+        for (int k=1; k < MAX_LOG; k++)
+            for (int v=0; v<n; v++)
+                if (ancestor[v][k-1] != -1) {
+                    // Move 2^{k-1} up, then 2^{k-1} again.
+                    ancestor[v][k] = ancestor[ancestor[v][k-1]][k-1];
+                }
+    }
+
+    int query(int u, int v, int root=-1) {
+        // What is the lowest common ancestor of u, v?
+        // Extension: Make this query from any root vertex you want.
+        if (root != -1) {n
+            // TODOn
+        }n
+        // assume that u is higher up than v, to simplify the code belown
+        if (level[u] > level[v]) swap(u, v);n
+        // STEP 1: set u and v to be ancestors with the same leveln
+        for (int k=MAX_LOG-1, k>=0; k--)n
+            if (level[v] - (1 << k) >= level[u]) {n
+                // If v is 2^k levels below u, move it up 2^k levels.n
+                v = ancestor[v][k];n
+            }n
+        // We can be certain that level[u] = level[v]. Reason: binary representation of all natural numbers.n
+        // Do we need to move to step 2?n
+        if (u == v) return un
+        // STEP 2: find the highest ancestor where u != v. Then the parent is the LCAn
+        for (int k=MAX_LOG; k>=0; k--)n
+            if (ancestor[u][k] != ancestor[v][k]) {n
+                // Move up 2^k stepsn
+                u = ancestor[u][k];n
+                v = ancestor[v][k];n
+            }n
+        return parent[u];n
+    }
+
+    T dist(int u, int v) {
+        // Find the distance between two vertices - very simple if we have LCA.
+        // TODO
+    }
+}
+

Nice! That’s the main functionality of LCA completed.

Corrolaries

Let’s quickly tackle the two remaining implementations:

  • Calculating the distance between two vertices u and v is the same as calculating the distance between u and query(u, v), and adding that to the distance between v and query(u, v)
  • Calculating the LCA from a particular root, just requires a slight change in perspective. For two vertices u and v, and custom root r, the LCA will always be one of query(u, v), query(u, r) or query(v, r).
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+
class LCA:
+"""
+vertices are represented as numbers 0->n-1.
+"""
+
+    # number such that 2^{MAX_LOG} > n. 20 works for n <= 10^6.
+    MAX_LOG = 20
+
+    def __init__(self, n_vertices):
+        self.n = n_vertices
+        self.adjacent = [[] for _ in range(self.n)]
+
+    def add_edge(self, u, v, weight=1):
+        self.adjacent[u].append((v, weight))
+        self.adjacent[v].append((u, weight))
+
+    def dfs(self, source, c_parent, c_level, c_length):
+        # Search from the source down the tree and set parent, level, length accordingly.
+        self.parent[source] = c_parent
+        self.level[source] = c_level
+        self.length[source] = c_length
+        for child, weight in self.adjacent[source]:
+            if child != c_parent:
+                self.dfs(child, source, c_level + 1, c_length + weight)
+
+    def build(self, root):
+        # Once edges are added, build the tree/data structure.
+        self.parent = [None]*self.n
+        self.level = [None]*self.n
+        self.length = [None]*self.n
+        self.dfs(root, -1, 0, 0)
+        self.ancestor = [[-1]*self.MAX_LOG for _ in range(self.n)]
+        # Initial step: ancestor[v][0] = parent[v]
+        for v in range(self.n):
+            self.ancestor[v][0] = self.parent[v]
+        # Now, compute ancestor[v][k] from 1->MAX_LOG
+        for k in range(1, self.MAX_LOG):
+            for v in range(self.n):
+                if self.ancestor[v][k-1] != -1:
+                    # Move 2^{k-1} up, then 2^{k-1} again.
+                    self.ancestor[v][k] = self.ancestor[self.ancestor[v][k-1]][k-1]
+
+    def query(self, u, v, root=None):
+        # What is the lowest common ancestor of u, v?
+        # Extension: Make this query from any root vertex you want.
+        if root is not None:
+            # Custom root -- see diagrams below for reasoning.n
+            a = self.query(u, v)n
+            b = self.query(u, root)n
+            c = self.query(v, root)n
+            # Case 1: root is in the same component as u when `a` is removed from the tree. So `b` is the LCAn
+            if (a == c and c != b) return bn
+            # Case 2: root is in the same component as v when `a` is removed from the tree. So `a` is the LCAn
+            if (a == b and c != b) return cn
+            # Case 3: b and c are above a in the tree. So return an
+            return an
+        # assume that u is higher up than v, to simplify the code below
+        if self.level[u] > self.level[v]:
+            u, v = v, u
+        # STEP 1: set u and v to be ancestors with the same level
+        for k in range(self.MAX_LOG-1, -1, -1):
+            if (self.level[v] - (1 << k) >= self.level[u]):
+                # If v is 2^k levels below u, move it up 2^k levels.
+                v = self.ancestor[v][k]
+        # We can be certain that level[u] = level[v]. Reason: binary representation of all natural numbers.
+        # Do we need to move to step 2?
+        if (u == v): return u
+        # STEP 2: find the highest ancestor where u != v. Then the parent is the LCA
+        for k in range(self.MAX_LOG-1, -1, -1):
+            if (self.ancestor[u][k] != self.ancestor[v][k]):
+                # Move up 2^k steps
+                u = self.ancestor[u][k]
+                v = self.ancestor[v][k]
+        return self.parent[u]
+
+    def dist(self, u, v):
+        # Find the distance between two vertices
+
+        return self.length[u] + self.length[v] - 2 * self.length[self.query(u, v)]n
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+
template<typename T = int> struct LCA {
+    // vertices are represented as numbers 0->n-1.
+    // number such that 2^{MAX_LOG} > n. 20 works for n <= 10^6.
+    int MAX_LOG = 20;
+    int n; vector<vector<pair<int, T> > > adjacent;
+    vi parent, level;
+    vvi ancestor;
+    vector<T> length;
+
+    LCA(int n_vertices) : n(n_vertices), adjacent(n), parent(n), level(n), length(n) { }
+
+    void add_edge(int u, int v, T weight=1) {
+        adjacent[u].emplace_back(v, weight);
+        adjacent[v].emplace_back(u, weight);
+    }
+
+    void dfs(int source, int c_parent, int c_level, T c_length) {
+        // Search from the source down the tree and set parent, level, length accordingly.
+        parent[source] = c_parent;
+        level[source] = c_level;
+        length[source] = c_length;
+        for (auto v: adjacent[source])
+            if (v.first != c_parent)
+                dfs(v.first, source, c_level+1, c_length+v.second);
+    }
+
+    void build(int root=0) {
+        // Once edges are added, build the tree/data structure.
+        dfs(root, -1, 0, 0);
+        // Compute ancestor
+        ancestor.assign(n, vi(MAX_LOG, -1));
+        // Initial step: ancestor[v][0] = parent[v]
+        for (int v=0; v<n; v++)
+            ancestor[v][0] = parent[v];
+        // Now, compute ancestor[v][k] from 1->MAX_LOG
+        for (int k=1; k < MAX_LOG; k++)
+            for (int v=0; v<n; v++)
+                if (ancestor[v][k-1] != -1) {
+                    // Move 2^{k-1} up, then 2^{k-1} again.
+                    ancestor[v][k] = ancestor[ancestor[v][k-1]][k-1];
+                }
+    }
+
+    int query(int u, int v, int root=-1) {
+        // What is the lowest common ancestor of u, v?
+        // Extension: Make this query from any root vertex you want.
+        if (root != -1) {
+            // Custom root -- see diagrams below for reasoning.n
+            int a = query(u, v);n
+            int b = query(u, root);n
+            int c = query(v, root);n
+            // Case 1: root is in the same component as u when `a` is removed from the tree. So `b` is the LCAn
+            if (a == c and c != b) return b;n
+            // Case 2: root is in the same component as v when `a` is removed from the tree. So `a` is the LCAn
+            if (a == b and c != b) return c;n
+            // Case 3: b and c are above a in the tree. So return an
+            return a;n
+        }
+        // assume that u is higher up than v, to simplify the code below
+        if (level[u] > level[v]) swap(u, v);
+        // STEP 1: set u and v to be ancestors with the same level
+        for (int k=MAX_LOG-1, k>=0; k--)
+            if (level[v] - (1 << k) >= level[u]) {
+                // If v is 2^k levels below u, move it up 2^k levels.
+                v = ancestor[v][k];
+            }
+        // We can be certain that level[u] = level[v]. Reason: binary representation of all natural numbers.
+        // Do we need to move to step 2?
+        if (u == v) return u
+        // STEP 2: find the highest ancestor where u != v. Then the parent is the LCA
+        for (int k=MAX_LOG; k>=0; k--)
+            if (ancestor[u][k] != ancestor[v][k]) {
+                // Move up 2^k steps
+                u = ancestor[u][k];
+                v = ancestor[v][k];
+            }
+        return parent[u];
+    }
+
+    T dist(int u, int v) {
+        // Find the distance between two vertices
+        return length[u] + length[v] - 2 * length[query(u, v)];n
+    }
+}
+

And that’s our implementation done! Now get out there and solve some problems!

Related Problems

This post is licensed under GNU GPL V3 by the author.
diff --git a/posts/mod/index.html b/posts/mod/index.html new file mode 100644 index 0000000..42d0c96 --- /dev/null +++ b/posts/mod/index.html @@ -0,0 +1,89 @@ + Modular Arithmetic | Monash Code Binder
Posts Modular Arithmetic
Post
Cancel

Modular Arithmetic

What is it?

Modular Arithmetic encompasses all sorts of theorems and optimizations surrounding the % operator in C and Python.

As you’ll see in the related problems, modulo arithmetic is often tied together in a question regarding counting things (combinatorics) or probabilities.

For those who haven’t come up across it before, % is an operation normally performed on two integers \(a\) and \(b\), and \(a\ \%\ b\) can be intuitively though of as the remainder when you divide \(a\) by \(b\).

For positive integers, this gives us the relation

\[a = a\ //\ b + a\ \%\ b\]

Where \(//\) denotes floor division.

C++ vs Python

The % operator actually does slightly different things in Python and C++. In particular, when the left hand side is a negative number, while Python will always return a positive number, C++ will always return a negative number!

1
+
print(-4 % 3)
+
1
+2
+3
+
int main() {
+  cout << (-4 % 3) << endl;
+}
+

The Python code will output 2, while the C++ code will output -1.

Characteristics

While % at first might not seem to have much use, it does have some very interesting characteristics.

For most of this we’ll be assuming that both numbers are positive, just because most contest problems do and its easier to wrap your head around.

Addition, Subtraction, Multiplication

The % operator can have its ordered swapped with any of the above operations. In particular:

\[\begin{eqnarray} (a + b)\ \%\ c &= ((a\ \%\ c) + (b\ \%\ c))\ \%\ c,\nonumber \\ (a - b)\ \%\ c &= ((a\ \%\ c) - (b\ \%\ c))\ \%\ c,\nonumber \\ (a * b)\ \%\ c &= ((a\ \%\ c) * (b\ \%\ c))\ \%\ c. \end{eqnarray}\]

Multiples

\(a\ \%\ b = 0\) iff \(b\) is a factor of \(a\).

Inverse

For some particular \(a\) and \(b\), with \(b\) prime, let \(a^{-1}\) denote the modular inverse of \(a\). The modular inverse of \(a\) is the only number between \(1\) and \(b-1\) such that \(a*a^{-1}\ \%\ b = 1\).

As long as \(b\) is prime (or at least \(a, b\) are coprime), this inverse will always exist and there will always only be one of them.

We’ll come back to inverses later because they pop up in a few questions, often regarding probabilities.

Fermat’s Little Theorem

For prime \(p\), we know the following is true for any \(a\):

\[a^p\ \%\ p = a\ \%\ p.\]

In particular, we also have

\[a^{p-2} \times a\ \%\ p = 1,\]

so \(a^{p-2}\) is \(a^{-1}\).

Computing things

Exponentials

The most common application of modular arithmetic is simply because the expected output would normally be way too large to store in a long long or something similar, and so the question asks to output the answer modulo (%) 100000007 or some other number (This one happens to be prime).

To compute \(a^b\ \%\ m\), you might think this requires us to do \(O(b)\) calculation, but in fact we can do it in \(O(\log(B))\).

1
+2
+3
+4
+5
+6
+7
+8
+9
+
def expmod(a, b, m):
+  res = 1 % m
+  a %= m
+  while b:
+    if (b & 1):
+      res = (res * a) % m
+    a = (a*a) % m
+    b //= 2
+  return res
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
typedef __int128 big;
+
+big expmod(big a, big b, big m) {
+  big res=1%m;
+  a %= m;
+  for (; b; b /= 2) {
+    if (b&1) {
+      res=(res*a)%m;
+    }
+    a=(a*a)%m;
+  }
+  return res;
+}
+

This is just a modification to the normal integer exponent algorithm, utilising the fact that we can decompose

\[a^b = \prod_{i=0}^k a^{b_i2^i},\quad b = \sum_{i=0}^k b_i2^i.\]

The binary decomposition of \(b\).

Modular Inverse

1
+2
+3
+4
+5
+6
+7
+
# works for any a, m coprime
+def inv(a, m):
+  _, x, y = gcd(m, a)
+  return y % m
+# works for m prime
+def inv(a, m):
+  return expmod(a, m-2, m)
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
// works for any a, m coprime
+ll inv(ll a, ll m) {
+  ll x, y;
+  gcd(m, a, x, y);
+  // Ensure outcome is positive. CPP exclusive.
+  return ((y % m) + m) % m;
+}
+// works for m prime
+ll inv(ll a, ll m) {
+  return expmod(a, m-2, m);
+}
+

Here gcd is the function that takes two numbers \(a\) and \(b\), and returns:

  • The greatest common factor of \(a\) and \(b\)
  • Two numbers \(x\) and \(y\) such that \(ax + by = \text{gcd}(a, b)\).

This works because \(ya = \text{gcd}(m, a) - xm\). But, for \(m\) prime, we have \(\text{gcd}(m, a) = 1\):

\[ya\ \%\ m = (1 - xm)\ \%\ m = 1.\]

And so \(y\ \%\ m\) has to be the multiplicative inverse of \(a\).

Fractions and Modular inverses

The final, and often most crucial trick goes as follows:

A common contest problem ends with the following statement:

The output can be expressed as an irreducible fraction \(\frac{p}{q}\). Output \(pq^{-1}\) modulo 100000007.

While this can seem daunting, \(pq^{-1}\) has some nice properties. Let’s see them:

Properties

Addition

Consider two fractions \(\frac{a}{b}\) and \(\frac{c}{d}\). We have:

\[\frac{a}{b} + \frac{c}{d} = \frac{ad + bc}{bd},\]

and (modulo \(m\)):

\[ab^{-1} + cd^{-1} = b^{-1}d^{-1}(ad + bc) = (ad + bc)(bd)^{-1}.\]

Multiplication

Consider two fractions \(\frac{a}{b}\) and \(\frac{c}{d}\). We have:

\[\frac{a}{b} \times \frac{c}{d} = \frac{ac}{bd},\]

and (modulo \(m\)):

\[ab^{-1} \times cd^{-1} = acb^{-1}d^{-1} = ac(bd)^{-1}.\]

Factorisation

Consider for the last time a single reducible fraction \(\frac{ka}{kb}\). We have:

\[(ka)(kb)^{-1} = kak^{-1}b^{-1} = ab^{-1}.\]

Wrap up

So, given the above, rather than having to store \(\frac{a}{b}\) for ludicrously sized \(a\) and \(b\), we can instead compute \(ab^{-1}\) and do arithmetic with these values.

As an example, if we wanted to compute \(\frac{p}{q} = (\frac{a}{b} + \frac{c}{d}) \times \frac{e}{f}\), then we could instead output

\[(ab^{-1} + cd^{-1}) \times ef^{-1}\ \%\ m.\]

How cool is that!

This post is licensed under GNU GPL V3 by the author.
diff --git a/posts/problems-21-s2-c1/index.html b/posts/problems-21-s2-c1/index.html new file mode 100644 index 0000000..f2210a4 --- /dev/null +++ b/posts/problems-21-s2-c1/index.html @@ -0,0 +1,559 @@ + Challenge Problems - 2021 Sem 2, Contest 1 | Monash Code Binder
Posts Challenge Problems - 2021 Sem 2, Contest 1
Post
Cancel

Challenge Problems - 2021 Sem 2, Contest 1

Sports Loans

Statement

Andrew is head of the sports club, and manages the inventory. Part of Andrew’s job is loaning footballs to people, and collecting those footballs once they have been used.

At the start of the day, Andrew has \(r\) footballs in stock, and knows that \(p+q\) people will approach him over the course of the day. \(p\) people will request a football, while \(q\) people will return a football. What Andrew does not know is the order in which these people will approach him.

Of course, Andrew wants to be able to give a football to everyone who requests one, when they request one. So for example if the first \(r+1\) people want a football, Andrew can’t give a football to the last person.

Andrew wants to know the probability that the above situation does not occur today, in other words the probability that every time someone requests a football, Andrew has one in stock.

Input / Output

Input will consist of three space separated integers, \(p, q\) and \(r\), as defined in the problem statement. Output should be a single number, the probability that Andrew will always be able to give a football to anyone who requests it. This number should have absolute error less than \(10^{-8}\).

Example Run

Input

1
+
4 1 3
+

Output

1
+
0.8
+

Since there is a 20% chance that the “return a football” event occurs before all 4 “request a football” events, which would cause problems.

Hints / Solution

Hint 1

Simulating won’t be enough, because of the sizes of p, q and r. What you should instead do is try to find a general form for the probability, based on p, q, r.

Start by trying to come up with a visualisation of this process in 2-dimensional space. What do good/bad orderings of people look like?

Hint 2

The visualisation we are looking for is one where we begin at point \((r, 0)\), and for each person, we either move to the right 1 unit (person returns a football), or up 1 unit (person requests a football).

A run is then invalid when we cross (not touch) the line \(y = x\). For an invalid run, what happens when we flip all points across the line \(y = x + 1\) before the intersection?

What is the total number of paths from this new starting point to \((q+r, p)\)?

Solution

To handle the easy cases, if \(p \leq r\), then the probability is 1 (We can’t possibly run out of footballs). If \( p > r + q\), then the probability is 0 (We can’t possibly handle everyones request). We assume neither of these is the case for the following discussion.

Let’s first view the problem through a different lens, to make the solution a bit more natural. Imagine Andrew is a point on the 2D plane. Whenever a person approaches him to give him a football, he moves one unit in the positive \(x\)-axis, and whenever a person approaches him to request a football, he moves one unit in the positive \(y\)-axis.

We can think of this as the \(x\)-axis representing footballs returned, while the \(y\) axis represents footballs taken. Since we start with \(r\) footballs, it might make sense to start Andrew at the position \((r, 0)\). Then, when Andrew is on the line \(y = x\), we know that Andrew has exactly 0 balls in inventory. Therefore we want to know the probability that Andrew never dips above this line (or equivalently, that Andrew never touches the line \(y = x + 1\)).

Since any ordering of the $p+q$ people is equally likely, we can simply count all possible distinct paths from \((r, 0)\) to \((r+q, p)\), and the proportion of these paths which sit below the line \(y = x+1\) is the probability we want. The number of all possible paths is \({p+q \choose p} = {p+q \choose q}\), since we can just pick the locations of the \(p\) (or \(q\)) people in our ordering.

Now, counting the number of paths that avoid the line is tough, but we can do something similar by finding a bijection between invalid paths and some other collecion.

Rather than considering paths from \((r, 0)\) to \((r+q, p)\), what if we instead started from the same point, reflected on the line \(y = x+1\)? Then we’d be looking at paths from \((-1, r+1)\) to \((r+q, p)\). Note that every path between these two points needs to touch the line \(y = x+1\). Furthermore, we can turn each of these paths into an invalid path from \((r, 0)\) to \((r+q, p)\) in the following way:

  1. Find the first intersection of the path with the line \(y = x + 1\) (Some intersection must exist).
  2. Mirror the path along the line \(y = x + 1\) before this intersection.

Since \((-1, r+1)\) is the mirrored version of \((r, 0)\), all of these new paths are distinct paths from \((r, 0)\) to \((r+q, p)\). Furthermore, since the original paths hit the line, each of the these mirrored paths also hit the line and are therefore invalid. Hopefully it’s also easy to see that every possible invalid path can be reached via this mirror method.

Therefore, the total number of invalid paths is equal to the total number of any of path between \((-1, r+1)\) and \((r+q, p)\). By the same argumentation as before, there are \({(r+q+1) + (p-r-1) \choose r+q+1} = {p+q \choose p - r - 1}\) possible paths.

Now that we know how many paths there are in total, and how many paths are invalid, we can calculate some probabilities. The probability that Andrew does run into this situation (That we have a bad path) is:

\[P(\text{bad}) = \frac{p+q \choose p - r - 1}{p+q \choose p} = \frac{(p+q)!p!q!}{(p+q)!(p-r-1)!(q+r+1)!} = \frac{\prod^r_{i=0}p-i}{\prod^r_{i=0}q+i+1}.\]

The probability the question asks for is then just \(P(\text{good}) = 1 - P(\text{bad})\). Note that the cancellation above is required to fit within precision and time limits, as we can’t compute \(p!\) for large enough \(p\) within time.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
# Read 3 ints
+p, q, r = list(map(int, input().split()))
+if p <= r:
+    print(1)
+elif p > r + q:
+    print(0)
+else:
+    p_bad = 1
+    for i in range(r+1):
+        p_bad *= (p-i) / (q+i+1)
+    # Be safe with 10 precision points.
+    print(f"{1-p_bad:.10f}")
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+
#include <iostream>
+#include <iomanip>
+
+using namespace std;
+
+int main() {
+
+    // p, q can be large, use long long.
+    long long p, q, r;
+
+    cin >> p >> q >> r;
+
+    if (p <= r) {
+        cout << 1 << endl;
+    } else if (p > r + q) {
+        cout << 0 << endl;
+    } else {
+        double p_bad = 1;
+
+        for (int i=0; i<= r; i++) {
+            p_bad = p_bad * ((double)(p-i)) / ((double)(q+i+1));
+        }
+
+        cout << setprecision(10) << fixed << 1 - p_bad << endl;
+    }
+
+    return 0;
+}
+

Optimal Farming

Statement

Amy has just bought a farm in the outback, and wants to start selling tomatoes. Some of the crops in the farm are already tomatoes, but there are others she wants to get rid of and replace with tomatoes.

Amy has employed the help of Square Tomatoes Group™. Amy can pay the group $\(s\) to plant an \(s \times s\) grid of crops with tomatoes (It doesn’t matter if the existing crop was tomatoes or not, all grid squares become tomatoes. This square can also exceed Amy’s farm). Amy wants to minimise her cost to Square Tomatoes Group™ such that all crops are tomatoes.

Input / Output

Input starts with two integers \(1 \leq l, w \leq 30\), the length and width of the farm, separated by space. Input then contains \(l\) lines, each containing a string of \(w\) characters. Each of these characters represent a grid square in the farm. This square is a tomato crop if and only if the character printed is a T.

Output should be a single integer, the minimum Amy has to pay to fill her farm with tomato crops.

Example Test

Input

1
+2
+3
+4
+
3 4
+PWTT
+TCTT
+TTTL
+

Output

1
+
3
+

Explanation: We can pay Square Tomatoes Group™ \($2\) to plant tomatoes in the top-left 2x2 area, and then \($1\) to plant tomatoes in the final bottom-right square.

Hints / Solution

Hint 1

Note that if every row and column has a square with no tomato, then the answer is rather obvious. The problem only gets interesting when an entire row/column is already tomatoes.

Hint 2

Suppose we don’t go for the easy solution of just using a massive square to cover our farm, and have a cheaper solution. Then one of the rows or columns in the farm must be untouched. How can we recurse from here?

Solution

We will generalise and compute \(\text{cost}(x1, x2, y1, y2)\), the cost of converting the rectangle \([x1, x2), [y1, y2)\) all to tomatoes. The question is asking us to compute \(\text{cost}(0, w, 0, l)\).

Note that we can always spend \($\text{max}(x2-x1, y2-y1)\) and cover the rectangle, by using a square that exceeds the bounds. Also, note that if a collection of squares overlaps every column of the rectangle, then the cost of planting all of these squares must be at least \(x2-x1\), and similarly if every row of the rectangle is overlapped by a square, the minimum cost is \(y2-y1\).

With this in mind, suppose there was a cheaper selection of squares that converts this entire rectangle to tomatoes. Then from the logic above, there must be some column or row which is not touched by these planted squares (A column or row that is already all tomatoes). This column/row separates our rectangle in two, and so we can solve the subproblem of \(\text{cost}\) on each of these rectangles.

For example, in the input given, we have a \(3 \times 4\) rectangle. The easy solution is to cover the entire thing with a \(4 \times 4\) square, costing \($4\).

But, column 2 is all tomatoes, so we can solve the subproblem on the left and right hand sides of this column, and see if doing this is cheaper. Continuing along, we find the left subproblem costs \($2\), and the right subproblem costs \($1\), and so the final result is \($3\).

We can achieve this within the time limit with Dynamic Programming.

The recursive definition is:

\[\text{cost}(x1, x2, y1, y2) := \text{min} \begin{cases} (x2 - x1) \times (y2 - y1) &\\ \text{cost}(x1, c, y1, y2) + \text{cost}(c, x2, y1, y2) & x1 < c < x2, \text{column } c \text{ from } y1 \to y2 \text{ all tomato.}\\ \text{cost}(x1, x2, y1, r) + \text{cost}(x1, x2, r, y2) & y1 < r < y2, \text{row } r \text{ from } x1 \to x2 \text{ all tomato.} \end{cases}\]

The base case being that \(\text{cost}(x1, x1+1, y1, y1+1) = b\), where \(b\) is 0 if it’s a tomato plant, and 1 otherwise. In order to know when an entire segment of a row/column is all tomatoes, we can also use DP, by breaking up each square into individual rows / columns.

For both of these we have \(l^2w^2\) values to compute, and each of the values takes \(l + w\) operations to compute (In the recursive definition, we might recurse for every row and column in the square). So the total cost is about \(30^5 \approx 2 \times 10^7\)

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+
import sys
+# Maximum recursion for this problem is actually like 60, but better safe than sorry.
+sys.setrecursionlimit(10000)
+
+l, w = list(map(int, input().split()))
+
+grid = [input() for _ in range(l)]
+
+# is the rectangle from x1 to x2, y1 to y2 all tomato? (RHS exclusive)
+tomato_dp = [[[[None for _1 in range(31)] for _2 in range(31)] for _3 in range(31)] for _4 in range(31)]
+def all_tomato(x1, x2, y1, y2):
+    if tomato_dp[x1][x2][y1][y2] is not None:
+        return tomato_dp[x1][x2][y1][y2]
+    if x1 < x2 - 1:
+        tomato_dp[x1][x2][y1][y2] = all_tomato(x1, x2-1, y1, y2) and all_tomato(x2-1, x2, y1, y2)
+    elif y1 < y2 - 1:
+        tomato_dp[x1][x2][y1][y2] = all_tomato(x1, x2, y1, y2-1) and all_tomato(x1, x2, y2-1, y2)
+    else:
+        # y2 = y1+1, x2 = x1+1.
+        tomato_dp[x1][x2][y1][y2] = grid[x1][y1] == "T"
+    return tomato_dp[x1][x2][y1][y2]
+
+cost_dp = [[[[None for _1 in range(31)] for _2 in range(31)] for _3 in range(31)] for _4 in range(31)]
+def cost(x1, x2, y1, y2):
+    if cost_dp[x1][x2][y1][y2] is not None:
+        return cost_dp[x1][x2][y1][y2]
+    if x1 == x2 or y1 == y2:
+        # Empty rectangle.
+        return 0
+    cur_min = max(x2-x1, y2-y1)
+    # Otherwise, there is an empty row/column we can exclude. Simply solve this suproblem.
+    for c in range(x1, x2):
+        if all_tomato(c, c+1, y1, y2):
+            cur_min = min(cost(x1, c, y1, y2) + cost(c+1, x2, y1, y2), cur_min)
+    for r in range(y1, y2):
+        if all_tomato(x1, x2, r, r+1):
+            cur_min = min(cost(x1, x2, y1, r) + cost(x1, x2, r+1, y2), cur_min)
+    cost_dp[x1][x2][y1][y2] = cur_min
+    return cost_dp[x1][x2][y1][y2]
+
+print(cost(0, l, 0, w))
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+
#include <iostream>
+#include <string>
+
+#define FOR(i,j,k) for(int i=j; i<k; i++)
+#define MAX(a, b) (a > b) ? a : b
+#define MIN(a, b) (a < b) ? a : b
+#define MAXN 30
+
+using namespace std;
+
+int l, w;
+
+const int UNKNOWN = -1;
+
+int DP_TOMATO[MAXN+1][MAXN+1][MAXN+1][MAXN+1];
+int DP_COST[MAXN+1][MAXN+1][MAXN+1][MAXN+1];
+string grid[MAXN+1];
+
+bool tomato(int x1, int x2, int y1, int y2) {
+    // Is the rectangle [x1, x2), [y1, y2) already tomato?
+    if (DP_TOMATO[x1][x2][y1][y2] != UNKNOWN)
+        return DP_TOMATO[x1][x2][y1][y2];
+    if (x1 < x2 - 1) {
+        // Look at the column x=x2-1 separately
+        DP_TOMATO[x1][x2][y1][y2] = tomato(x1, x2-1, y1, y2) && tomato(x2-1, x2, y1, y2);
+    } else if (y1 < y2 - 1) {
+        // Look at the row y=y2-1 separately
+        DP_TOMATO[x1][x2][y1][y2] = tomato(x1, x2, y1, y2-1) && tomato(x1, x2, y2-1, y2);
+    } else {
+        // We are a 1x1.
+        DP_TOMATO[x1][x2][y1][y2] = grid[x1][y1] == 'T';
+    }
+    return DP_TOMATO[x1][x2][y1][y2];
+}
+
+int cost(int x1, int x2, int y1, int y2) {
+    if (DP_COST[x1][x2][y1][y2] != UNKNOWN)
+        return DP_COST[x1][x2][y1][y2];
+    if (x1 == x2 || y1 == y2)
+        // Empty rectangle. Possible in the below recursion so just return 0.
+        return 0;
+    // We can always cover the rectangle by using a big square.
+    int cur_min = MAX(x2-x1, y2-y1);
+    FOR(c,x1,x2)
+        if (tomato(c, c+1, y1, y2))
+            // If this column is tomato, then we can try solving the two subproblems instead by removing the column.
+            cur_min = MIN(
+                cur_min,
+                cost(x1, c, y1, y2) + cost(c+1, x2, y1, y2)
+            );
+    FOR(r,y1,y2)
+        if (tomato(x1, x2, r, r+1))
+            // If this row is tomato, then we can try solving the two subproblems instead by removing the row.
+            cur_min = MIN(
+                cur_min,
+                cost(x1, x2, y1, r) + cost(x1, x2, r+1, y2)
+            );
+    DP_COST[x1][x2][y1][y2] = cur_min;
+    return DP_COST[x1][x2][y1][y2];
+}
+
+int main() {
+
+    cin >> l >> w;
+    FOR(i,0,l) {
+        cin >> grid[i];
+    }
+
+    FOR(x1,0,l+1)FOR(x2,0,l+1)FOR(y1,0,w+1)FOR(y2,0,w+1) {
+        DP_TOMATO[x1][x2][y1][y2] = UNKNOWN;
+        DP_COST[x1][x2][y1][y2] = UNKNOWN;
+    }
+
+    cout << cost(0, l, 0, w) << endl;
+
+    return 0;
+}
+

Repetitive Jugglers

Statement

Alice is the leader of a juggling crew, and they are set to perform a crazy juggling trick.

In this trick, every member of the crew starts off with a different coloured ball. Every member then picks another member of the crew (possibly themselves), let us call that member their receiver.

Then, the trick begins. Every second, every crew member will throw all of the balls they are holding to their designated receiver.

The trick only stops once everyone has the same ball they started with (Note that not always does this trick stop!)

Alice wants to know, given who has chosen who as receiver, whether the game will end, and if so, how many seconds this will take.

Input

Input will consist of two lines.

The first line will contain an integer \(n\), the number of members in the juggling crew.

The second line will then contain \(n\) space-separated integers. The \(i\)th integer represents the \(i\)th crew member’s pick for receiver.

(So we enumerate crew members \(1, 2, 3\ldots\), and if the second integer is \(1\), that means that crew member \(2\) has chosen \(1\) as their receiver.)

It is guaranteed that if the trick does stop, it will stop before \(10^{15}\) seconds have passed

Output

If the trick will never finish, print \(-1\). Otherwise, print the total length of the trick, in seconds.

Example Test

Input

1
+2
+
3
+2 1 3
+

Output

1
+
2
+

After 1 second, person 1 and person 2 throw the balls at each other, and person 3 throws the ball to themselves. As such person 1 is holding person 2’s ball, and person 2 is holding person 1’s ball. Person 3 is holding their own ball.

After 2 seconds, the same action occurs, and so everyone is holding their own ball.

Hints / Solution

Hint 1

Since this trick might continue for \(10^{15}\) seconds, we cannot simulate it (Especially with large \(n\)).

We need to figure out ahead of time when this will occur.

Notice that if one person receives 2 or more balls at any point in time, the trick will never end.

Hint 2

Only selections of “receivers” in which every member is the receiver of exactly one member will finish, and they will always finish.

For math inclined individuals, these receivers represent a permutation of the group, and we want to know how many repeated applications of this permutation are needed to take us back to the identity.

We can figure out how long this will take based on cycles that are present in the permutation.

Solution

Note that if any member recieves two juggling balls, then our sequence can never return to how it was. Therefore everyone must recieve a single ball every second. In other words, our \(n\) space-separated integers must be a permutation of the numbers \(1\) through to \(n\). Note that in a permutation, there are multiple distinct cycles of different sizes (For example 1 passes to 3 passes to 7 passes to 4 passes to 1). Notably, everyone in these cycles has their ball every \(k\) seconds, where \(k\) is the length of the cycle.

Therefore, if we have cycles of length \(k_1, k_2, \ldots k_a\), then the first time the entire sequence will repeat must be the least common multiple of these values \(k_1, k_2, \ldots k_a\) (The first number \(c\), which is divisible by all of \(k1, k2, \ldots, k_a\)).

So our solution just needs to find each of these cycles, and count their length. Then compute the least common multiple.

In the sample input, we have a permutation with 2 cycles (1 passes to 2 passes to 1, and 3 passes to 3). These cycles are of length 2 and 1 respectively.

Therefore every 2 seconds, members 1 and 2 will have their balls, and every second, member 3 will have their ball. Because of this, the answer is the smallest number which is divisible by 2 and 1 (2).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+
n = int(input())
+# make it 0 -> n-1.
+choices = list(map(lambda x: int(x)-1, input().split()))
+
+# Greatest common divisor
+def gcd(a, b):
+    if b == 0:
+        return abs(a)
+    return gcd(b, a%b)
+
+# Least common multiple
+def lcm(a, b):
+    return a * b // gcd(a, b)
+
+# list(set()) will remove duplicates. If no duplicates, then we have a permutation
+if len(list(set(choices))) == n:
+    # Permutation
+    # Find the cycle lengths
+    lengths = []
+    found = [False]*n
+    for x in range(n):
+        # Is x not already in a cycle?
+        if not found[x]:
+            length = 0
+            # Search through the cycle.
+            cur = x
+            while not found[cur]:
+                found[cur] = True
+                length += 1
+                # Move to the next person
+                cur = choices[cur]
+            lengths.append(length)
+    # Print the lcm of all lengths.
+    cur_lcm = 1
+    for length in lengths:
+        # The lcm of a list is simply the pairwise lcm of each element, combined.
+        cur_lcm = lcm(cur_lcm, length)
+    print(cur_lcm)
+else:
+    # Not a permutation
+    print(-1)
+
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+
#include <vector>
+#include <iostream>
+#include <map>
+
+using namespace std;
+
+typedef long long ll;
+typedef vector<ll> vll;
+
+ll gcd(ll a, ll b) {
+    if (b == 0) return a;
+    return gcd(b, a%b);
+}
+
+ll lcm(ll a, ll b) {
+    return (a / gcd(a, b)) * b;
+}
+
+int main() {
+
+    int n;
+    cin >> n;
+
+    vll nums(n);
+    for (int i=0; i<n; i++) {
+        cin >> nums[i];
+        // Make it 0 -> n-1.
+        nums[i]--;
+    }
+
+    // Check for duplicates
+    bool bad = false;
+    map<int, int> dup_check;
+    for (int i=0; i<n; i++) {
+        dup_check[i] = 0;
+    }
+    for (int i=0; i<n; i++) {
+        dup_check[nums[i]]++;
+        if (dup_check[nums[i]] > 1) {
+            // Someone recieves 2.
+            bad = true;
+        }
+    }
+    if (bad) {
+        cout << -1 << endl;
+    } else {
+        vll lengths;
+        vll found(n, false);
+        for (int i=0; i<n; i++) {
+            if (!found[i]) {
+                int length = 0;
+                int cur = i;
+                while (!found[cur]) {
+                    found[cur] = true;
+                    length++;
+                    cur = nums[cur];
+                }
+                lengths.push_back(length);
+            }
+        }
+        ll cur_lcm = 1;
+        for (ll l: lengths) {
+            cur_lcm = lcm(cur_lcm, l);
+        }
+        cout << cur_lcm << endl;
+    }
+
+    return 0;
+}
+
This post is licensed under GNU GPL V3 by the author.
diff --git a/posts/uf/index.html b/posts/uf/index.html new file mode 100644 index 0000000..ae7df79 --- /dev/null +++ b/posts/uf/index.html @@ -0,0 +1,871 @@ + Union Find / DSU | Monash Code Binder
Posts Union Find / DSU
Post
Cancel

Union Find / DSU

Where is this useful?

In many problems, translating into a graph structure can prove helpful, as we can describe our problem in very abstract terms.

Once you’ve translated into this graph structure, often you might want to know whether two vertices are connected via a path, and if this is not the case, what two separate components they come from. Union Find allows us to not only answer this question, but slowly add edges to the graph and still answer these queries fast.

As such, Union Find is useful in any problem where connections are incrementally being added to some structure, and along the way you need to query what vertices are connected.

Implementing the Data Structure

Basics

Let’s first define the interface for our Union Find. We want to provide the ability to merge two vertices, and we should be able to query two vertices, asking if they are connected.

At first, every vertex is disconnected. We can add edges later as need be.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
class UnionFind:
+    """
+    vertices are represented as numbers 0->n-1.
+    """
+
+    def __init__(self, n):
+        self.n = n
+
+    def merge(self, a, b) -> bool:
+        # Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+        pass # TODO
+
+    def connected(self, a, b) -> bool:
+        # Whether the two vertices a and b are connected.
+        pass # TODO
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
struct UnionFind {
+    // vertices are represented as numbers 0->n-1.
+    int n;
+
+    UnionFind(int n_verts) : n(n_verts) { }
+
+    bool merge(int a, int b) {
+        // Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+        // TODO
+    }
+
+    bool connected(int a, int b) {
+        // Whether the two vertices a and b are connected.
+        // TODO
+    }
+};
+

Now, we can take our first approach at the data structure. Notice that before any merging occurs, each component is uniquely identified by a single vertex contained within. As we merge our vertices, we’ll try keep it that way.

In order to do this, we can model each component as a rooted tree. The root of this tree is the identifier, and so from any vertex in the tree, we can get to the identifier by moving up the tree.

To merge two components (trees), we simply place the second tree as a child of the first tree. The second root no longer identifies a component, and the first root is now the identifier of the combined component.

So, to implement this, we’ll create a parent array, which contains the parent of each vertex. For vertices that are the root, they will be their own parents.

We will also need a method to find the identifier of any component, by moving up the tree. We will do this with find in the code.

And we can already get around to implementing connected and merge. For connected, a and b are in the same component if the identifier of their components are the same. For merge, we simply need to modify the parent attribute of one identifier, so that it points to the root of the other component:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+
class UnionFind:
+    """
+    vertices are represented as numbers 0->n-1.
+    """
+
+    def __init__(self, n):
+        self.n = n
+        # parent[x] = x to begin with.n
+        self.parent = list(range(n))n
+
+    def find(self, a):n
+        # Find the root of this componentn
+        if self.parent[a] == a:n
+            return an
+        return self.find(self.parent[a])n
+
+    def merge(self, a, b) -> bool:
+        # Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+
+        a = self.find(a)n
+        b = self.find(b)n
+        if a == b:n
+            return Falsen
+        self.parent[b] = an
+        return Truen
+
+    def connected(self, a, b) -> bool:
+        # Whether the two vertices a and b are connected.
+
+        a = self.find(a)n
+        b = self.find(b)n
+        return a == bn
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+
struct UnionFind {
+    // vertices are represented as numbers 0->n-1.
+    int n;
+    vector<int> parent;n
+
+    UnionFind(int n_verts) : n(n_verts), parent(n_verts) {m
+        iota(parent.begin(), parent.end(), 0);n
+    }
+
+    int find(int a) {n
+        // Find the root of this componentn
+        if (parent[a] == a) return a;n
+        return find(parent[a]);n
+    }n
+
+    bool merge(int a, int b) {
+        // Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+        a = find(a);n
+        b = find(b);n
+        if (a == b) return false;n
+        parent[b] = a;n
+        return true;n
+    }
+
+    bool connected(int a, int b) {
+        // Whether the two vertices a and b are connected.
+        a = find(a);n
+        b = find(b);n
+        return a == b;n
+    }
+};
+

Useful data

A keen eye might’ve spotted that there’s possibility of some bad complexity coming out of these methods. If components are merged badly (So that we have a very unbalanced tree) we can make it so that find (and therefore merge/connected) are O(n) complexity. To improve this, and to make the data structure more useful as a whole, let’s take a quick detour and try to include some other data as part of our data structure:

  • size: This should be an array which stores the size of each component. The size entry for non-identifier vertices doesn’t matter and can be left with old data.
  • rank: This should be an array which stores the maximum depth of any component tree. The rank entry for non-identifier vertices doesn’t matter and can be left with old data.

It could be a good bit of practice to try this yourself; Modify the methods above to store and update the size and rank values.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+
class UnionFind:
+    """
+    vertices are represented as numbers 0->n-1.
+    """
+
+    def __init__(self, n):
+        # Number of components
+        self.n = n
+        # parent[x] = x to begin with.
+        self.parent = list(range(n))
+        # size = number of vertices in componentn
+        self.size = [1]*nn
+        # rank = max-depth of component treen
+        self.rank = [1]*nn
+
+    def find(self, a):c
+        # Find the root of this component
+        if self.parent[a] == a:
+            return a
+        return self.find(self.parent[a])
+c
+    def merge(self, a, b) -> bool:
+        # Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+        a = self.find(a)
+        b = self.find(b)
+        if a == b:
+            return False
+        self.size[a] += self.size[b]n
+        self.parent[b] = a
+        self.rank[a] = max(self.rank[a], self.rank[b])n
+        if self.rank[a] == self.rank[b]:n
+            self.rank[a] += 1n
+        self.n -= 1n
+        return True
+
+    def connected(self, a, b) -> bool:c
+        # Whether the two vertices a and b are connected.
+        a = self.find(a)
+        b = self.find(b)
+        return a == b
+c
+    def size_component(self, a):n
+        # Find the size of a particular component.n
+        # Question: Why do we need to call `find`?n
+        return self.size[self.find(a)]n
+
+    def num_components(self):n
+        return self.nn
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+
struct UnionFind {
+    // vertices are represented as numbers 0->n-1.
+    int n;
+    vector<int> parent, size, rank;
+
+    UnionFind(int n_verts) : n(n_verts), parent(n_verts), size(n_verts, 1), rank(n_verts, 1) {m
+        iota(parent.begin(), parent.end(), 0);
+    }
+
+    int find(int a) {c
+        // Find the root of this component
+        if (parent[a] == a) return a;
+        return find(parent[a]);
+    }c
+
+    bool merge(int a, int b) {
+        // Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+        a = find(a);
+        b = find(b);
+        if (a == b) return false;
+        size[a] += size[b];n
+        parent[b] = a;
+        rank[a] = max(rank[a], rank[b]);n
+        if (rank[a] == rank[b]) rank[a]++;n
+        n--;n
+        return true;
+    }
+
+    bool connected(int a, int b) {c
+        // Whether the two vertices a and b are connected.
+        a = find(a);
+        b = find(b);
+        return a == b;
+    }c
+
+    int size_component(int a) {n
+        // Find the size of a particular component.n
+        // Question: Why do we need to call `find`?n
+        return size[find(a)];n
+    }n
+
+    int num_components() { return n; }n
+};
+

If the maximum of rank[a] and rank[b] is equal to rank[b], then the total depth in the tree will be at most rank[b]+1, since we must include the path from a to b, before considering any children of b.

Armed with this information, we can make some better decisions when it comes to merging, and also start compressing the trees.

Depth reduction

Since we get bad complexity when merging trees with large rank as children, let’s always pick the largest rank tree to be the identifier. Then the overall rank of the resultant tree only increases if the rank of the two original trees was the same.

Additionally, every time we call find, we are traversing up our tree. But in this traversal, it is very cheap to simply connect every vertex along the way to the root vertex, using recursion.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+
class UnionFind:
+    """
+    vertices are represented as numbers 0->n-1.
+    """
+
+    def __init__(self, n):c
+        # Number of components
+        self.n = n
+        # parent[x] = x to begin with.
+        self.parent = list(range(n))
+        # size = number of vertices in component
+        self.size = [1]*n
+        # rank = max-depth of component tree
+        self.rank = [1]*n
+c
+    def find(self, a):
+        # Find the root of this component
+        if self.parent[a] == a:
+            return a
+        # Whenever I call find, set the parent to be right above me.n
+        b = self.find(self.parent[a])n
+        self.parent[a] = bn
+        return bn
+
+    def merge(self, a, b) -> bool:
+        # Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+        a = self.find(a)
+        b = self.find(b)
+        if a == b:
+            return False
+        if (self.rank[a] < self.rank[b]):n
+            a, b = b, an
+        self.size[a] += self.size[b]
+        self.parent[b] = a
+        if self.rank[a] == self.rank[b]:
+            self.rank[a] += 1
+        self.n -= 1
+        return True
+
+    def connected(self, a, b) -> bool:c
+        # Whether the two vertices a and b are connected.
+        a = self.find(a)
+        b = self.find(b)
+        return a == b
+c
+    def size_component(self, a):c
+        # Find the size of a particular component.
+        # Question: Why do we need to call `find`?
+        return self.size[self.find(a)]
+c
+    def num_components(self):c
+        return self.n
+c
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+
struct UnionFind {
+    // vertices are represented as numbers 0->n-1.
+    int n;
+    vector<int> parent, size, rank;
+
+    UnionFind(int n_verts) : n(n_verts), parent(n_verts), size(n_verts, 1), rank(n_verts, 1) {
+        iota(parent.begin(), parent.end(), 0);
+    }
+
+    int find(int a) {
+        // Find the root of this component
+        if (parent[a] == a) return a;
+        // Whenever I call find, set the parent to be right above me.n
+        return parent[a] = find(parent[a]);m
+    }
+
+    bool merge(int a, int b) {
+        // Merge the two vertices a and b. Return a boolean which is true if they weren't already merged.
+        a = find(a);
+        b = find(b);
+        if (a == b) return false;
+        if (rank[a] < rank[b]) swap(a, b);n
+        size[a] += size[b];
+        parent[b] = a;
+        if (rank[a] == rank[b]) rank[a]++;
+        n--;
+        return true;
+    }
+
+    bool connected(int a, int b) {c
+        // Whether the two vertices a and b are connected.
+        a = find(a);
+        b = find(b);
+        return a == b;
+    }c
+
+    int size_component(int a) {c
+        // Find the size of a particular component.
+        // Question: Why do we need to call `find`?
+        return size[find(a)];
+    }c
+
+    int num_components() { return n; }
+};
+

Complexity Analysis

And that is all the changes required to reduce the complexity of union find, but how much has it done?

Well, to construct a rank 2 tree we need to merge 2 rank 1 trees, to construct a rank 3 tree we need to merge 2 rank 2 trees, and so on and so forth. Therefore in a union find with n vertices, we have at most log2(n) rank on each tree in our data structure.

This means that find is log(n), meaning both merge and connected are also log(n). (In fact, with the path compression above, the complexity is even less (inverse ackermann), but this isn’t super important under contest conditions)

And that’s the data structure fully taken care of. Now let’s solve some problems!

A simple application

Let’s try our hand at Friend Circle. Give it a shot yourself before reading the discussion below!

(Note: The time bounds for this problem are very small. Python will probably TLE. But give it a shot anyways!).

Hint

While the problem description is a bit sparse, hopefully you can spot that we care about what group of friends are connected by some friendship (If A and B are friends, and B and C are friends, then all 3 form a circle of friends, no need for A and C to be friends.)

So, if we let every person be a vertex in our graph, with edges representing friendship, then Union Find is exactly the tool we need. Before we get into coding we need only ask ourselves two things:

  1. What is the maximum size of our Union Find n?
  2. How will I turn people’s names into the digits 0 to n-1?

Solution

To answer 1, the maximum number of people is simply 2 times the total number of connections. For 2, we can use a dictionary/map to map strings to integers. To ensure every person is unique from 0 to n-1, we can start a counter at 0, and every time we see a new name, increment this counter. Then the old value of the counter is the id for that person:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
from collections import defaultdict
+t = int(input())
+
+for _ in range(t):
+    connections = int(input())
+    max_people = 2 * connectionsb
+    uf = UnionFind(max_people)b
+    cur_counter = 0
+    def count_increase():
+        global cur_counter
+        cur_counter += 1
+        return cur_counter - 1
+    # The defaultdict now assigns a new id to every new person mentioned.
+    person_map = defaultdict(count_increase)
+    for _ in range(connections):
+        p1, p2 = input().split()
+        uf.merge(person_map[p1], person_map[p2])b
+        print(uf.size_component(person_map[p1]))b
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
int main() {
+
+    int tests;
+    int connections;
+
+    cin >> tests;
+
+    for (int t_no=0; t_no<tests; t_no++) {
+        cin >> connections;
+        int max_people = connections * 2;b
+        UnionFind uf(max_people);b
+        int counter = 0;
+        map<str, int> person_map;
+        for (int c=0; c<connections; c++>) {
+            string c1, c2;
+            cin >> c1 >> c2;
+            if (person_map.count(c1) == 0) person_map[c1] = counter++;
+            if (person_map.count(c2) == 0) person_map[c2] = counter++;
+            uf.merge(person_map[c1], person_map[c2]);b
+            cout << uf.size_component(person_map[c1]) << endl;b
+        }
+    }
+
+    return 0;
+}
+

A slightly hidden application

Next, lets try a harder problem - Roads of NITT.

Have a go!

(Note: The input format is very weird (There’s some whitespace where there shouldn’t be). My current python solution fails for this reason)

Hint

This problem seems similar but different to the problem above. We are still asking about connectivity, but we are breaking connections rather than forming them :(.

Consider this though - Would you be able to solve the problem if it was told in reverse?

Solution

Looking at the problem in reverse, it seems we start off with a disconnected area, and then bit by bit, more connections are made. This is starting to look like Union Find!

So all we need to do is:

  1. Calculate what roads remain at the end of the problem
  2. Answer the queries in reverse, joining instead of destroying
  3. Reverse these results and print them

However, we need to be a bit careful about what our results are in the first place - We want to count how many pairs of hostels are disconnected - This is an N^2 operation. We can do this in N using union find (For every vertex, we know how many vertices it is connected to (and therefore not connected to)), but we still don’t want to do this for every query. Let’s start by calculating the correct value after all roads have been destroyed.

If a road is formed, how many old pairs of hostels are no longer disconnected? A hostel can only be connected now and disconnected before if one of the hostels was already connected to the LHS of the road, and the other hostel was already connected to the RHS of the road. The number of possible pairs here is the size of the component on the LHS of the road, times the size of the component on the RHS of the road.

So every time we see an R query, we just need to update our current count of disconnect pairs using the Union Find:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+
t = int(input())
+
+for _ in range(t):
+    n = int(input())
+    edges = []
+    for _ in range(n-1):
+        x, y = list(map(int, input().split()))
+        # 0-index
+        edges.append((x-1, y-1))
+    connected = [False] * len(edges)
+    q = int(input())
+    queries = []
+    for _ in range(q):
+        queries.append(input())
+        if queries[-1].startswith("R"):
+            connected[int(queries[-1].split()[1])-1] = False
+    uf = UnionFind(n)b
+    # Add all remaining roadsb
+    for a in range(len(edges)):b
+        if connected[a]:b
+            uf.merge(edges[a][0], edges[a][1])b
+    # First - calculate how many pairs of hostels are disconnected.
+    current = 0b
+    for x in range(n):b
+        current += n - uf.size_component(x)b
+    current //= 2b
+    # Answering time!
+    queries.reverse()
+    answers = []
+    for q in queries:
+        if q.startswith("Q"):
+            answers.append(current)
+        else:
+            edge_index = int(q.split()[1])-1
+            current -= uf.size_component(edges[edge_index][0]) * uf.size_component(edges[edge_index][1])b
+            uf.merge(*edges[edge_index])b
+    answers.reverse()
+    for a in answers:
+        print(a)
+    # Separate ouput by a space
+    print()
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+
vector<pair<int, int> > edges;
+vector<bool> connected;
+vector<int> queries;
+vector<int> answers;
+
+int main() {
+
+    int tests;
+    cin >> tests;
+
+    for (int t=0; t<tests; t++) {
+        edges.clear();
+        connected.clear();
+        queries.clear();
+        answers.clear();
+        int n;
+        cin >> n;
+        for (int i=0; i<n-1; i++) {
+            int x, y;
+            cin >> x >> y;
+            // 0-index
+            edges.push_back({x-1, y-1});
+        }
+        connected.assign(n-1, true);
+        int q;
+        cin >> q;
+        for (int i=0; i<q; i++) {
+            string s;
+            cin >> s;
+            if (s == "Q") {
+                queries.push_back(-1);
+            } else {
+                int a;
+                cin >> a;
+                queries.push_back(a-1);
+                connected[a-1] = false;
+            }
+        }
+        UnionFind uf(n);
+        // Add all remaining roads
+        for (int i=0; i<n-1; i++) {b
+            if (connected[i]) {b
+                uf.merge(edges[i].first, edges[i].second);b
+            }b
+        }b
+        // First - calculate how many pairs of hostels are disconnected.
+        int current = 0;b
+        for (int i=0; i<n; i++)b
+            current += n - uf.size_component(i);b
+        current = current / 2;b
+        // Answering Time!
+        reverse(queries.begin(), queries.end());
+        for (auto qn: queries) {
+            if (qn == -1) {
+                answers.push_back(current);
+            } else {
+                current = current - uf.size_component(edges[qn].first) * uf.size_component(edges[qn].second);b
+                uf.merge(edges[qn].first, edges[qn].second);b
+            }
+        }
+        reverse(answers.begin(), answers.end());
+        for (auto a: answers) {
+            cout << a << endl;
+        }
+        cout << endl;
+    }
+
+    return 0;
+}
+
This post is licensed under GNU GPL V3 by the author.
diff --git a/redirects.json b/redirects.json new file mode 100644 index 0000000..2f3c5c1 --- /dev/null +++ b/redirects.json @@ -0,0 +1 @@ +{"/norobots/":"https://monashaps.github.io//404.html","/assets/":"https://monashaps.github.io//404.html","/posts/":"https://monashaps.github.io//404.html"} \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..9a60a0b --- /dev/null +++ b/robots.txt @@ -0,0 +1,5 @@ +User-agent: * + +Disallow: /norobots/ + +Sitemap: https://monashaps.github.io//sitemap.xml diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..f091779 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,72 @@ + + + +https://monashaps.github.io//posts/dp/ +2021-12-13T11:01:10+11:00 + + +https://monashaps.github.io//posts/mod/ +2021-04-01T22:07:32+11:00 + + +https://monashaps.github.io//posts/factorization/ +2021-04-05T21:02:26+10:00 + + +https://monashaps.github.io//posts/lca/ +2021-12-13T11:01:10+11:00 + + +https://monashaps.github.io//posts/problems-21-s2-c1/ +2021-08-23T11:00:00+10:00 + + +https://monashaps.github.io//posts/uf/ +2021-12-27T13:53:13+11:00 + + +https://monashaps.github.io//posts/dsless-editorial/ +2023-12-28T23:02:40+11:00 + + +https://monashaps.github.io//categories/ +2023-12-28T23:03:47+11:00 + + +https://monashaps.github.io//tags/ +2023-12-28T23:03:47+11:00 + + +https://monashaps.github.io//archives/ +2023-12-28T23:03:47+11:00 + + +https://monashaps.github.io//about/ +2023-12-28T23:03:47+11:00 + + +https://monashaps.github.io// + + +https://monashaps.github.io//tags/difficulty-2/ + + +https://monashaps.github.io//tags/difficulty-3/ + + +https://monashaps.github.io//categories/data-structures/ + + +https://monashaps.github.io//categories/math/ + + +https://monashaps.github.io//categories/trees/ + + +https://monashaps.github.io//categories/contests/ + + +https://monashaps.github.io//assets/img/comp_assets/MCPC_Editorial.pdf +2023-12-28T23:03:29+11:00 + + diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..dc2c062 --- /dev/null +++ b/sw.js @@ -0,0 +1 @@ +self.importScripts('/assets/js/data/cache-list.js'); var cacheName = 'chirpy-20231228.2303'; function isExcluded(url) { const regex = /(^http(s)?|^\/)/; /* the regex for CORS url or relative url */ for (const rule of exclude) { if (!regex.test(url) || url.indexOf(rule) != -1) { return true; } } return false; } self.addEventListener('install', (e) => { self.skipWaiting(); e.waitUntil( caches.open(cacheName).then((cache) => { return cache.addAll(include); }) ); }); self.addEventListener('fetch', (e) => { e.respondWith( caches.match(e.request).then((r) => { /* console.log('[Service Worker] Fetching resource: ' + e.request.url); */ return r || fetch(e.request).then((response) => { return caches.open(cacheName).then((cache) => { if (!isExcluded(e.request.url)) { /* console.log('[Service Worker] Caching new resource: ' + e.request.url); */ cache.put(e.request, response.clone()); } return response; }); }); }) ); }); self.addEventListener('activate', (e) => { e.waitUntil( caches.keys().then((keyList) => { return Promise.all(keyList.map((key) => { if(key !== cacheName) { return caches.delete(key); } })); }) ); }); diff --git a/tags/difficulty-2/index.html b/tags/difficulty-2/index.html new file mode 100644 index 0000000..d9bdb61 --- /dev/null +++ b/tags/difficulty-2/index.html @@ -0,0 +1 @@ + Difficulty 2 | Monash Code Binder
Home Tags Difficulty 2
Tag
Cancel
diff --git a/tags/difficulty-3/index.html b/tags/difficulty-3/index.html new file mode 100644 index 0000000..a5607db --- /dev/null +++ b/tags/difficulty-3/index.html @@ -0,0 +1 @@ + Difficulty 3 | Monash Code Binder
Home Tags Difficulty 3
Tag
Cancel
diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..1ff22e2 --- /dev/null +++ b/tags/index.html @@ -0,0 +1 @@ + Tags | Monash Code Binder
Home Tags
Tags
Cancel