CCF CSP-S 2024 提高組初賽解析

HaneDaniko發表於2024-09-26

Certified Software Professional - Senior 非專業級軟體能力認證測試

本解析不提供閱讀程式與完善程式題目的程式碼,如有需要請透過 luogu.com.cn 相關連結 下載

如有謬誤煩請指正

答案

AACBB
BDABD
ACBCD
✓××BC
✓✓✓BCC
✓×✓CAC
AAAAA
AABAA

單項選擇

1

在 Linux 系統中,如果你想顯示當前工作目錄的路徑,應該使用哪個命令?

A pwd
B cd
C ls
D echo

pwd : print working directory
cd : 跳轉到指定目錄
ls : 列出當前目錄的所有子檔案和子資料夾
echo : 輸出指定內容

2

假設一個長度為n的整數陣列中每個元索值互不相同,且這個陣列是無序的。要找到這個陣列中最大元素的時間複雜度是多少?

A \(O(n)\)
B \(O(logn)\)
C \(O(nlogn)\)
D \(O(1)\)

無序的陣列只能透過兩兩比較來查詢,需要比較 \(n-1\) 次(第一次不需要比較),因此為 \(O(n)\)

3

在 C++中,以下哪個函式呼叫會造成溢位?

A int foo(){ return 0;}
B int bar(){int x=1;return x; }
C void baz(){ int a[1000];baz();)
D void qux(){ return; }

函式棧溢位一般有兩種:函式內變數開的太大,或者函式遞迴深度太深

這裡 baz() 函式重複呼叫自身,無終止條件,因此會溢位

4

在一場比賽中,有 \(10\) 名選手參加,前三名將獲得金、銀、牌。若不允許並列,且每名選手只能獲得一枚獎牌,則不同的頒獎方式有多少種?

A 120
B 720
C 504
D 1000

不妨選出一個長度為 \(3\) 的序列,依次獲得金,銀,銅牌,則方案有 \(A^{3}_{10}=10\times 9\times 8=720\)

5

下面哪個資料結構最適合實現先進先出(FIFO)的功能?

A 棧
B 佇列
C 線性表
D 二叉搜尋樹

棧是先進後出

後面兩種資料結構不具備存放與彈出基本資料的功能

線性表在功能上類似陣列

6

已知 \(f(1)=1\),且對於 \(n\ge2\)\(f(n)=f(n-1)+f(⌊n/2⌋)\) ,則 \(f(4)\) 的值為:

A \(4\)
B \(5\)
C \(6\)
D \(7\)

\(f(2)=f(1)+f(1)=2\)

\(f(3)=f(2)+f(1)=3\)

\(f(4)=f(3)+f(2)=5\)

7

假設有一個包含 \(n\) 個頂點的無向圖,且該圖是尤拉圖。以下關於該圖的描述中哪一項不一定正確?

A 所有頂點的度數均為偶數
B 該圖連通
C 該圖存在一個尤拉回路
D 該圖的邊數是奇數

尤拉圖定義:僅由尤拉回路構成的圖

尤拉回路:即一筆能畫完的圖。形式化地說,尤拉回路是從任意一個點出發,不重複經過任何一條邊,也不遺漏任何一條邊,最終仍能回到該節點的路徑

根據定義,D 是錯誤的,反例為一個正方形

8

對陣列進行二分查詢的過程中,以下哪個條件必須滿足?

A 陣列必須是有序的
B 陣列必須是無序的
C 陣列長度必須是 \(2\) 的冪
D 陣列中的元素必須為整數

二分查詢本質上也是一種二分答案,需要保證答案具有單調性

9

考慮一個自然數 \(n\) 以及一個數 \(m\),你需要計算 \(n\) 的逆元(即 \(n\)\(m\) 意義下的乘法逆元),下列哪種演算法最為適合?

A 使用暴力法依次嘗試
B 使用擴充套件歐幾里得演算法
C 使用快速冪法
D 使用線性篩法

逆元的定義是這樣的:

定義 \(a\) 在模 \(p\) 意義下的逆元 \(b\) 滿足 \((\frac{c}{a})\mod p=(c\times b)\mod p\)

\(p\) 為質數的時候,才能使用快速冪(+費馬小定理)來求逆元,而擴充套件歐幾里得演算法為更加通用的求逆元方法,只需要滿足兩個數互質

更詳細的求逆元方法可以透過 這篇文章 的 5.3.1 章瞭解

10

在設計一個雜湊表時,為了減少衝突,需要使用適當的雜湊函效和衝突解決策略。已知某雜湊表中有 \(n\) 個鍵值對,表的裝載因子為 \(a(0<a\le 1)\) 。在使用開放地址法解決衝突的過程中,最壞情況下查詢一個元素的時間複雜度為

A \(O(1)\)
B \(O(logn)\)
C \(O(1/(1-a))\)
D \(O(n)\)

開放地址法解決衝突:假設當前元素 \(x\) 需要放入地址 \(p\) 中,但現在 \(p\) 位置已經有了元素,直接放置就會導致衝突,因此考慮向後依次找,找到第一個沒有放置元素的位置放置,這樣可以防止衝突(遍歷到最後再從頭開始)

因此最壞情況下需要把整個表遍歷一遍,複雜度為 \(O(n)\)

科普裝載因子的定義:裝載因子定義了一個閾值,當雜湊表中的條目數超出了載入因子與當前容量的乘積時,則要對該雜湊表進行擴容、rehash操作(即重建內部資料結構),擴容後的雜湊表將具有兩倍的原容量。

11

假設有一棵 \(h\) 層的完全二叉樹,該樹最多包含多少個結點?

A \(2^h-1\)
B \(2^{h+1}-1\)
C \(2^h\)
D \(2^{h+1}\)

因為每個節點都有兩個兒子,因此每一層的節點個數都是上一層的兩倍

總結點數為 \(1+2^{1}+2^{2}+\cdots+2^{h-1}=2^{h}-1\)

12

設有一個 \(10\) 個頂點的完全圖,每兩個頂點之間都有一條邊。有多少個長度為 \(4\) 的環?

A \(120\)
B \(210\)
C \(630\)
D \(5040\)

因為圖完全聯通,任取四個點一定能保證它們構成一個環

因為環上的點是無序的,因此很多人會考慮計算 \(C^{4}_{10}=\frac{10\times 9\times 8\times 7}{4\times 3\times 2\times 1}=210\),然而這樣是錯誤的

考慮是什麼地方出了問題

剛才我們說,任取四個點一定能保證它們構成一個環

但是環 1 2 3 4 和環 2 1 3 4 並不是同一個環,前者有 1->2->3 的連邊,後者有 2->1->3 的連邊,顯然不是同一個環

正確的做法是考慮重複的環:

注意到 1 2 3 4 2 3 4 1 3 4 1 2 4 1 2 3 是重複的,以此類推,每一種可能的組合都有四種可能的起始節點,因此會重複計算 \(4\)

另外,注意到 1 2 3 4 4 3 2 1 是重複的,同樣,上面的每一種反過來和原來都是一樣的,因此答案為 \(\frac{A^{4}_{10}}{2\times 4}=630\)

13

對於一個整數 \(n\)。定義 \(f(n)\)\(n\) 的各位數字之和。問使 \(f(f(x))=10\) 的最小自然數 \(x\) 是多少?

A \(29\)
B \(199\)
C \(299\)
D \(399\)

代選項即可

\(f(29)=11,f(f(29))=2\)

\(f(199)=19,f(f(199))=10\)

\(f(299)=20,f(f(299))=2\)

\(f(399)=21,f(f(399))=3\)

14

設有一個長度為 \(n\) 的 01 字串,其中有 \(k\)\(1\) ,每次操作可以交換相鄰兩個字元。在最壞情況下將這 \(k\)\(1\) 移到字串最右邊所要的交換次數是多少?

A \(k\)
B \(\frac{k\times (k-1)}{2}\)
C \((n-k)\times k\)
D \(\frac{(2n-k-1)\times k}{2}\)

D 這個數的來源:令全部數字都排在最左邊,然後從右到左一個一個挪,然後認為答案是 \((n-1)+(n-2)+(n-3)\cdots+(n-k)\)

實際上,在排後面的數字時,由於右邊已經有排好的數字,不需要將新來的數字與排好的數字進行比較與交換了,所以每個數字的交換次數只有 \((n-k)\) 次,一共 \(k(n-k)\)

15

如圖是一張包含 \(7\) 個頂點的有向圖,如果要除其中一些邊,使得從節點 \(1\) 到節點 \(7\) 沒有可行路徑,且刪除的邊數最少,請問總共有多少種可行的刪除邊的集合?

A \(1\)
B \(2\)
C \(3\)
D \(4\)

一個可能更醜的圖

上來先注意到 \(\{8,9\}\) 是合法的,並且不存在只刪一個邊的解法,因此最優解為 \(2\)

可行的邊的集合

\(\{8,9\}\)
\(\{5,6\}\)
\(\{6,8\}\)
\(\{1,6\}\)

閱讀程式

1

分析

logic() 函式內的位運算,採用列舉法來判斷

  1. \(x=0,y=0\) 時,(0 & 0) ^ ((0 ^ 0) | (~0 & 0))=0 ^ (0 | 0)=0
  2. \(x=0,y=1\) 時,(0 & 1) ^ ((0 ^ 1) | (~0 & 1))=0 ^ (1 | 1)=1
  3. \(x=1,y=0\) 時,(1 & 0) ^ ((1 ^ 0) | (~1 & 0))=0 ^ (1 | 0)=1
  4. \(x=1,y=1\) 時,(1 & 1) ^ ((1 ^ 1) | (~1 & 1))=1 ^ (0 | 0)=1

因此為邏輯或運算

觀察到 recursion() 函式非常像快速排序演算法,但是有最大遞迴深度限制,最多隻會進行 depth 層遞迴,因此 這個排序演算法的結果不一定有序

因為快速排序最多隻會進行 \(\log n\) 層,因此當 \(d\ge \log b\) 時,輸出的序列是有序的

題目

  • \(1000\ge d\ge b\) 時,輸出的序列是有序的

\(d\ge b\ge \log b\),正確

  • 當輸入 5 5 1 時,輸出為 1 1 5 5 5

透過暴力模擬可以發現,初始陣列為 5 5 1 1 5,排序函式只進行了一層,排序不完全,輸出值為 5 1 1 5 5

  • 假設陣列 c 長度無限制,該程式所實現的演算法的時間複雜度是 \(O(b)\)

該排序演算法在每一層都花費 \(O(b)\) 遍歷了一遍陣列,然而該演算法共有 \(d\) 層,因此複雜度為 \(O(bd)\)

  • 函式 int logic(int x,int y) 的功能是

按位或,詳見上方解析

  • 當輸入為 10 100 100 時,輸出的第 \(100\) 個數為

\(d\ge \log b\),可知輸出陣列是有序的,也即讓我們求一個最大的數

嘗試代選項,判斷哪個數可以透過 \(0\)\(99\) 內的數 \(i\) 透過 (a|i)%(b+1) 得到,可以發現 \(98\) 不合法(不存在 \(a\) 中為 \(1\) 的二進位制位),而 \(95\) 合法,因此為 \(95\)

詳細地說,二進位制意義下 a=1010,而 98=1100010,顯然,如果一個數為 a|i 的形式,那麼第四位一定是 \(1\),類似地,98+101=11000111 同樣不合法(需要考慮這個是因為 (98+101)%101=98),因此 \(98\) 不合法

2

分析

觀察 solve() 函式的 dp 部分

發現 int k = (j<<1)|(s[i]-'0') 這一句,其實就相當於是在 \(j\) 的後面再拼一個數字,然後用拼接前的數字去更新拼接後的數字,因為拼接上的數字都是 s[i] 中的內容,i 為正序遍歷,初態為 \(0\),那麼我們發現,該 dp 陣列一直在嘗試將 s 中新的字元拼接到已有的字元後面,並且這樣的拼接是有序的,只能將後面的字元拼接到前面的字元後面,綜合這三個條件我們可以總結出:這個 dp 陣列是在統計 s 的子序列方案數

但是這個方案數是有限制的,下面我們對這些限制來進行一些描述

  1. 注意到只有在 \([0,2^{m-1}-1]\) 內的數字才能用於對答案進行更新,對應到二進位制就是所有不大於 \(m\) 位的二進位制數,因此,s 中大於 \(m\) 位的子序列是不會被統計到的
  2. 此外的一個限制條件是 if(j !=0 || s[i]=='1'),這是這個函式區別於 solve2() 的最大不同點,這意味著當一個子序列開頭是 \(0\) 的時候,由於 j=0,k=0,因此無法進行更新,也就是說 這個函式要求子序列的開頭必須是 \(1\)

隨後函式統計了所有 i*dp[i] 的值,值*方案數=總和

根據以上條件,我們可以總結出該函式的功能:找出 s 全部以 \(1\) 開頭的,不超過 \(m\) 位的子序列的十進位制值總和

下面來看 solve2(),它則列舉了全部 \([0,2^{n}-1]\) 的全部情況,轉為二進位制即為全部不超過 \(n\) 位的二進位制數,隨後,如果二進位制位為 \(1\),就把該位上的 s 拼接到已有的字元後面(num = num * 2 + (s[j]-'0') 一句),可以發現 solve2() 同樣是在找子序列的值,那麼我們來說一下 solve2() 的限制

  1. 注意到,雖然我們列舉了所有可能的情況,但只有 cnt<=m 的數才能對答案進行更新,也就是說最終子序列中最多隻能拼接 \(m\) 個數

也就是說這個函式的功能為:找出 s 全部不超過 \(m\) 位的子序列的十進位制值總和

可以發現,兩者的差距就在是否以 \(1\) 開頭

題目

  • 假設陣列 dp 長度無限制,函式 solve() 所實現的演算法的時間複雜度是 \(O(n2^{m})\)

solve() 函式的核心演算法與複雜度瓶頸為兩層 for 迴圈,根據 solve() 函式的 for 迴圈邊界,可以得出其複雜度為 \(T(n2^{m-1})\),即 \(O(n2^{m})\)(相當於乘以一個常數 \(2\)

  • 輸入 11 2 10000000001 時,程式輸出兩個數 3223

對於 solve2()

\(m=1\) 時:11 個數,貢獻為 \(2\times 1+9\times 0=2\)

\(m=2\) 時:10\(9\) 種,01\(9\) 種,11\(1\) 種,貢獻為 \(2\times 9+1\times 9+3=30\)

總貢獻 \(2+30=32\)

對於 solve()

\(m=1\) 時:11 個數,貢獻為 \(2\times 1+9\times 0=2\)

\(m=2\) 時:10\(9\) 種,01\(0\) 種(不合法),11\(1\) 種,貢獻為 \(2\times 9+1\times 0+3=21\)

總貢獻 \(2+21=23\)

故正確,這道題最大的坑點就是實際輸出的時候 solve2() 在前

  • \(n\le 10\) 時,solve() 的返回值始終小於 \(4^{10}\)

可以發現其子序列貢獻最多為 \(\sum^{10}_{i=1}C^{i}_{10}\times (2^{i}-1)\) 個(這裡的做法不是本題所需要的,如果你想知道為什麼是這個式子,可以去下面 "當 \(n\le 6\) 時,solve() 的最大可能返回值為" 那道題去找)

但是考場上顯然我們不好算這麼大的數,因為我們找出的所有子序列都小於 \(1111111111\),而我們一共有 \(2^{10}\) 個數,而每個數都不會超過 \(2^{10}\),因此一定小於 \(2^{10}\times 2^{10}=2^{20}=4^{10}\),因此正確,這也是出題人把這個數設成 \(4^{10}\) 的意圖

  • \(n=10\)\(m=10\) 時,有多少種輸入使得兩行的結果完全一致

根據剛才的分析,不能存在任何以 \(0\) 開頭,且有值的子序列,也就是說,只要序列中出現了 \(0\),必須保證後面的數都沒有值,即都是 \(0\)

不難發現 00000000001000000000110000000011100000001111000000111110000011111100001111111000111111110011111111101111111111 都符合要求,共 \(11\)

  • \(n\le 6\) 時,solve() 的最大可能返回值為

考慮極限情況,即 \(111111\),且 \(m\ge n\),此時每一位對答案都會有最大貢獻,即答案也最大,那麼,位數為 \(1\) 的子序列共有 \(C^{1}_{6}\) 個,而因為每一位都為 \(1\),因此每一個的值都為 \(1\),同理,位數為 \(2\) 的子序列共有 \(C^{2}_{6}\) 個,每一個的值都為 \(3\),以此類推,可以發現其子序列貢獻最多為 \(\sum^{6}_{i=1}C^{i}_{6}\times (2^{i}-1)=665\)

  • \(n=8,m=8\),solve 和 solve2 的返回值最大可能的差值為

和上面那個結果完全一致的題正好反過來了,這道題裡我們需要一個以 \(0\) 開頭的子序列值最大的序列,那麼容易想到的是這個序列開頭一定是 \(0\),為了使後面的值最大,我們構造出 01111111 這樣的序列作為答案

題目讓我們計算的差值,實際上也就是以 \(0\) 開頭的子序列權值和,去掉開頭的 \(0\),也就相當於求 \(n=7,m=7\)1111111 的子序列權值和,又回到剛才那個問題,答案為 \(\sum^{7}_{i=1}C^{i}_{7}\times (2^{i}-1)=2059\)

3

分析

首先來看 \(p\) 陣列,可以發現其用於埃氏篩(init() 函式內區域性程式碼),最後篩出來,\(p_{i}=1\) 則表示 \(i\) 為素數

然後觀察變數 const P1,P2,B1,B2,K1,K2

其中 P1,P2 用於取模,而後面這兩組變數,一組用於給 H 賦初值,另一組用來生成 p1,p2 陣列(在主函式中),發現其格式類似 線性同餘,判斷作用為生成隨機數,也就是說,p1,p2,H 三種變數的起始值均是隨機的,推測是雜湊演算法

對於這個雜湊結構體 H,我們可以觀察出如下這幾點:

  • H 定義的加法不滿足交換律
  • H 中有兩個雜湊引數 h1,h2,另外一個引數 l 記錄合併的次數,同時也參與運算

下方的選擇題問我們,H 的合併方式看起來像什麼,後面列出的選項全都是樹上操作,據此推測該演算法是樹雜湊,一個節點的初值由該節點編號是否為質數來決定,一個節點最終的雜湊值由其初值,左子樹值,右子樹值共同決定,當兩個節點對應的子樹(這裡指子樹結構和子樹上節點的 p 陣列狀態)完全相同時,這兩個節點的雜湊值就相等

隨後程式進行了排序與去重,這並不在樹雜湊的演算法範圍內,用處在下方題目中會有體現

題目

  • 假設程式執行前能自動將 maxn 改為 n+1,所實現的演算法的時間複雜度是 \(O(n\log n)\)

總複雜度為篩法+排序+去重= \(T(n\log\log n+n\log n+n)=O(n\log n)\)

  • 時間複雜度的瓶頸在 init()

init(): \(T(n+n\log\log n)\)

solve(): \(T(2n+n\log n)\)

顯然 solve() 更大

  • 若修改常數 B1K1 的值,該程式可能會輸出不同的結果

程式第一行輸出的是雜湊值,儘管修改雜湊內部的引數並不影響雜湊判等(不考慮雜湊衝突),但仍然會導致雜湊值改變,因此正確

  • 在 solve() 函式中,h[] 的合併順序可以看做是

\(2i\) 是左子樹,\(i\) 是當前,\(2i+1\) 是右子樹,則 h[i]=h[2*i]+h[i]+h[2*i+1] 表示 左-中-右,即中序遍歷

  • 輸入 \(10\),輸出的第一行是

暴力模擬雜湊值即可透過本題

但是顯然有更好的解法,考慮到這個雜湊值在擴充套件的時候,每次都是乘 \(2^{l}\),再加上一個新的雜湊值,這是一個非常好的性質,假如我們把某一位的雜湊值看做一個 \(m\) 位的二進位制數,這樣的合併方式就會告訴我們,這個二進位制數對應的 \(l\) 一定等於 \(m\),因為初態時,對於 \(l=1\) 的節點,值恰好就是 \(1\),因此每次合併的時候,都會正好將左右兩邊的 \(h1\) 值拼接起來,形成一個新的二進位制數

因此我們可以直接利用這個性質,對 \(n=10\) 的完全二叉樹的中序遍歷 8 4 9 2 10 5 1 6 3 7 轉成二進位制數(該位編號是質數就為 \(1\),否則為 \(0\),詳見 H 的建構函式),即 \(0001010011_{d}=83\)

  • 輸入 16,輸出的第二行是

即問:\(n=16\) 時,子樹雜湊值不同的節點共有幾個(雜湊值不同在上方已有定義,即子樹結構或者子樹上節點的 p 陣列狀態存在不同)

可以發現 9 10 12 14 15 16 是相同的,11 13 是相同的,其餘都是不同的

因此為 \(16-(6-1)-(2-1)=10\)

完善程式

1

分析

upper_bound() 函式是有其固定作用的,含義為 “查詢有序序列中第一個大於給定元素的值的下標”,根據下面呼叫 upper_bound(b,b+n,...) 也可以發現,這裡的區間是左閉右開的

get_rank() 函式相當於二分答案裡的 check() 函式,這裡的作用是檢查當前和在所有 sum 裡的排名。這裡使用了一種寫法為 upper_bound(b,b+n,sum-a[i])-b,假設我們固定 \(a_{i}\),那麼在 \(b\) 陣列裡,比 \(sum-a_{i}\) 小的元素值,與 \(a_{i}\) 的和也一定比 \(sum\) 小,因此我們就將它加到當前 \(sum\) 的排名前,這是這段函式的含義

solve() 即為二分答案函式,二分可能的 \(sum\) 值,找出排名恰好為 \(k\) 的那個

程式相對比較簡單,主要的難點可能在二分的格式上,如果你平常用的二分不是 l=mid+1,r=mid 格式,那麼可能就容易選錯

題目

  • 選二分割槽間右端點,二分範圍是給定陣列,不少人會錯選 an-a-1,然而因為是左閉右開的,因此應該選 an-a

  • 現在我們要找的是第一個大於 \(a_{i}\) 的數字,注意到 r=mid 應該說明兩點,首先說明當前點是合法的(否則應該直接調到 \(mid-1\)),其次說明當前值大了,合法說明嚴格大於查詢值,因此選 a[mid] > ai

  • 返回查詢元素的地址,這裡查到了第 \(l\) 個元素,因為地址從 \(a+0\) 開始,因此返回 \(a+l\)

  • 仍然選二分割槽間右端點,由於二分的是 \(sum\) 的值,最大值應該是兩邊最大的元素相加,由於兩邊元素都單調不減,所以選最後兩個即可,注意最後的元素是 \([n-1]\) 不是 \([n]\)

  • 同第二問,我們來分析,首先當前答案是不合法的,其次當前答案的排名應該較小,因此需要將 \(sum\) 的值變大,不合法說明不能取等,因此選 get_rank(mid) < k

2

分析

嚴格次短路演算法,即找出嚴格大於最短路的第二短路,首先來分析次短路演算法的邏輯:

  • 跑最短路,分別記錄當前最短路與次短路
  • 如果當前沒有任何路徑,那麼新找到的路徑成為最短路
  • 如果新找到的路徑比當前最短路短,那麼新找到的成為最短路,之前的最短路成為次短路
  • 如果新找到的路徑小於最短路但大於次短路,那麼新找到的路徑成為次短路

這樣,我們就能夠找到最短路和次短路

題目中要求輸出路徑,因此推測 pre 陣列是記錄路徑的陣列,dis 陣列按照一般最短路演算法,記錄的是節點到起點的最短路,這裡比較奇怪的是兩個指標 pre2dis2,它們是屬於次短路的陣列,你可以理解成它們也是獨立的陣列,只不過用的是 predis 的空間

也就是說,dis[a]=(dis+n)[a-n]=dis2[a-n]dis2[a]=(dis+n)[a]=dis[a+n]

然後來看加邊,用的是鏈式前向星,加邊函式是 add

下面分析這個 upd 函式

寫過 dij 演算法的應該能看出來,這是一個鬆弛操作,因為其中有對 dis 的比較,賦值和優先佇列的 push(),並且能看出來,鬆弛成功之後函式會返回 true,否則為 false,函式的引數中,a 是節點的前驅編號,b 是節點編號,d 是更新的距離,p 是一個優先佇列

在 solve() 函式里跑的就是一個比較正常的 dij

題目

  • 這裡的目的是用當前最短路的 dis 去更新次短路的 dis,因為這兩個 dis 分在兩個不同的陣列裡,所以可能會不相同,但只有兩個陣列相同才能進行鬆弛操作,所以需要統一一下,那麼這裡的前驅顯然是 pre[b],節點編號應該是 n+b,因為我們要更新的是 dis2[b],因為 dis2=dis+n,所以這裡應該為 n+b,距離是最短路距離 dis[b]
  • 考的是 pair<> 的關鍵字問題,pair<> 在排序的時候會優先以第一關鍵字排序,相同時以第二關鍵字排序,那麼這裡我們顯然應該優先排節點 dis 而不是節點編號,所以應該是 d 在前,然後應該是 dis 小的在前,因為優先佇列把大的放在前面,所以為了實現小的在前面,我們應該插入一個負的,取出來的時候再把它變成正的就行了
  • 不少人直接會無腦選 0x3f,因為這個數好記,不會炸 int,並且夠大,但是這個題已經為我們定義了最大值 const int inf,所以我們直接照著 inf 值選 0x1f
  • 這個地方是已經鬆弛過最短路了,並且失敗了,現在要來嘗試鬆弛次短路,和上面第二題一樣,次短路編號為 n+b,因為我們還沒更新 dis2,所以這裡不能用 dis2[a]+c,只能用 dis[a]+c
  • 上面的判斷語句告訴我們現在 an 大(並且一定比 2n 小),所以我們直接調 dis2[a] 會越界(但是在這裡呼叫 dis[a] 就是可以的),為了不讓其越界,我們應該呼叫 dis2[a-n],即 dis2[a%n]

相關文章