跳转至

出題

出題前的準備

具備一定的水平

一方面,一個人自己出題,很難出出難度大於自身水平的題目,一定的 OI 水平有助於想到更加優質的 idea 並想出優秀的做法;另一方面,OI 水平在一定程度上代表着 OI 資歷,見識過更多的題目的選手也會對「好題」擁有自己的見解。

抱有認真負責的態度

出題是給別人做的,比起展示自己,更多是為了是服務他人。算法競賽是選手之間的競賽,而不是出題人與做題人之間的較量。因此,出題不應以考倒選手為目標(當然,適當的防 AK 與良好的區分度也是非常重要的),而應當讓選手能在比賽中有所收穫。花費足夠的時間精力去學習如何出題並認真負責地出題非常重要。

做好耗費大量時間的準備

如果想要認真地出題,就必然要花費大量的時間。如果不做好心理準備,可能導致比賽準備匆忙,質量不過關,也可能在事後由於沒有將時間花費在學習上而懊悔。但出題也可以帶來很多美好的回憶,如果真的對出題抱有興趣,並做好了充分的心理準備,出題帶來的收穫也能夠彌補那些花費的時間。

認真閲讀本文的內容

本文從如何出題、如何把題出好兩個方面對整個出題流程進行了介紹。對於想要出題的人來説,認真閲讀本文一定能夠受益匪淺。

題目內容

出一道題,idea,即題目本質的內容,是題目的靈魂,也是出題的第一步。

idea 的來源

  1. 受到已有題目的啓發(但不能照搬或無意義地加強,如:序列題目搬到仙人掌上)。
  2. 受到學過的知識點的啓發(但不能毫無聯繫地拼湊知識點)。
  3. 從生活/遊戲中受到啓發(但注意不要把遊戲出成大模擬)。
  4. 不知道為什麼,就是想到了一道題。

什麼樣的 idea 是不好的

關於原題

原題大致可分為完全一致、幾乎一致和做法一致三種。

  • 完全一致:使用一題的 AC 代碼可以 AC 另一題。
  • 幾乎一致:由一題的 AC 代碼改動至另一題的 AC 代碼可以由一個不會該題的人完成。
  • 做法一致:核心思路、做法一致,但代碼實現上、不那麼關鍵的細節上有差異。

這三種原題自下而上為包含關係。

以下情況不應出現:

  1. 在明知有「幾乎一致」的原題的情況下出原題。
  2. 由於未使用搜索引擎查找導致自己不清楚有原題,從而出了「幾乎一致」的原題。
  3. 在「做法一致」的原題廣為人知(如:NOIP、NOI 原題)時出原題。
  4. 在帶有選拔性的考試的非送分題中出現「做法一致」的原題。

以下情況最好不要出現:

  1. 在明知有至少為「做法一致」的原題的情況下出原題。
  2. 由於未使用搜索引擎查找導致自己不清楚有原題,從而出了「做法一致」的原題。
  3. 在任何情況下出「幾乎一致」的原題。

可以放寬要求的例外情況:

  1. 校內模擬賽。
  2. 以專題訓練為目的的模擬賽。
  3. 難度較低的比賽,或是定位為送分題的題目。

關於毒瘤題

「毒瘤題」是一個非常模糊而主觀的觀念,在這只是引用一些前人關於此的探討,加以自己的一些理解。這個話題是非常開放的,歡迎大家來發表自己的觀點。

一道好題不應該是兩道題拼在一起,一道好題會有自己的 idea——而它應該不加過多包裝地突出這個 idea。

一道好題應該新穎。真正的好題,應該是能讓人腦洞出新的好題的好題。

——vfk《UOJ 精神之源流》

例子:「XR-1」柯南家族,做法的前後兩部分完全割裂,前半部分為 「模板」樹上後綴排序,後半部分是經典樹上問題。就算是隨意輸入樹的點權,依然可以做第二部分,前後部分沒有聯繫。

一類 OI 題以數學為主,無論是題目描述還是做法都是數學題的特徵,並且解法中不含算法相關的知識點,這類 OI 題目統稱為純數學題。

——王天懿《論偏題的危害》

經典例子:NOIP2017 小凱的疑惑

OI 中的數學題與其它數學題的區別,也是體現 OI 本質的一個特點,是 OI 中的數學題往往重點不在答案 是什麼,而在如何 加快 答案的計算。如果一道題考察的重點是「怎麼算」而非「怎麼快速計算」,這樣的數學題一般都是不適合出在 OI 中的。

一部分偏題中牽涉到了大學物理的內容,導致選手在面對這些從未接觸過物理知識點時變得不知所措,造成了知識上的隔膜。

——王天懿《論偏題的危害》

經典例子:「清華集訓 2015」多邊形下海

不止是物理,OI 題目中不應過多涉及到其它學科的知識,如果涉及應當給予詳細的解釋,不應使其它學科的知識作為解題的重大障礙。

一道好題無論難度如何,都應該具有自己的思維難度,需要選手去思考並發現一些性質。

一道好題的代碼可以長,但一定不是通過強行嵌套或者增加條件而讓代碼變長,而是長得自然,讓人感覺這個題的代碼就應該是這麼長。

——王天懿《論偏題的危害》

經典例子:「SDOI2010」豬國殺「集訓隊互測 2015」未來程序·改

在一般的 OI 比賽中,思維難度應占主要部分。當然,如 THUWC/THUSC 的 Day 2+ 那樣的工程題也有其存在的道理——畢竟體驗營的目的除了考察選手的算法設計能力,還有和大學學習對接的工程代碼以及文檔學習能力。但在一般的 OI 比賽中,考察更多的應當還是算法設計與思維能力。

題面

使用 LaTeX 書寫公式

網上有很多 LaTeX 的教程,如:

使用時請注意 LaTeX 公式的格式要求

題目背景

題目背景最好儘量簡短。在題目背景較長時,應當與題目描述分開。

需要絕對避免題目背景嚴重影響題意的理解。

必要時,可以提供與背景結合的題目描述與簡潔的題目描述兩個版本。

題目描述

簡而言之,題目描述需要 清晰易懂

題面中的每個可能不被理解的定義都應得到解釋,不應憑空冒出未加定義的概念。例如:在 CF1172D Nauuo and Portals 中,你必須在題面中解釋什麼是「傳送門」。

題面中涉及到的每個概念應當使用單一的詞彙來描述。例如:不應一會兒説「費用」,一會兒説「代價」。

不應不加説明地使用與原義、常見義不同的詞彙。例如:不應不加説明地用「路徑」代指一條邊。

你需要保證你的題面不會自相矛盾。例如:在 CF1173A Nauuo and Votes 中,沒有把 "?" 作為一種 "result",是因為 "?" 的含義是 "there are more than one possible results"。

你需要保證你的題面不能被錯誤理解而自圓其説,即使這種理解是反常識、沒有人會這麼去想的。例如:在 CF1172D Nauuo and Portals 中,之所以要繁瑣地定義 "walk into" 並與 "teleport" 區分,是為了防止這種理解:通過傳送門可以到另一個傳送門,而到了傳送門會傳送,因此會反覆橫跳。

順着讀題目描述應當能看懂每一句話,並理解題目的任務與要求。至少在緊接着的下一段話中疑惑能夠得到解釋,而不是需要在若干段後才能得到解釋,或者要看了輸入輸出格式才能明白題意,甚至需要根據樣例來猜題意。例如:在 「GuOJ Round #1」琪露諾的冰雪宴會 中,在輸出格式才第一次出現了題目的目標「霧之湖最終能接收到的最大水量」,再加上「靈夢當然能很快算出來清理完全部小溪的總費用是多少」這句帶有誤解性質的話,更容易使人讀錯題意,這是不可取的,應當在題目描述中就對題目的目標進行説明。(在這個例子中還存在題目背景嚴重影響題意理解的問題。)相同的錯誤還出現在 CF1423(4)N Bubblesquare Tokens 中,在輸出格式才第一次出現了題目的目標 "friend pairs and number of tokens each of them gets on behalf of their friendship"。

輸入輸出格式

輸入輸出格式清晰 完整 即可,沒有死板的要求,個人建議參照 CF 的題目來寫輸入輸出格式,具體可以參考CF 出題人須知

為了方便選手做題,輸入輸出格式中最好説明每個變量的具體含義,除非變量的意義非常長,沒法一句話説清楚(這時可以説「意義見題目描述」)。

需要特別注意的是,如果輸出中含有小數,請儘量使用 SPJ 來對誤差的大小進行限制,而非要求「保留 x 位小數」。

「保留 x 位小數」對精度的要求可能是無限的。例如:要求保留三位小數,實際答案為 \(0.0015\),此時只要有任意大小的誤差導致計算出的答案小於 \(0.0015\),即使計算出的答案是 \(0.00149999\cdots\) 也會輸出錯誤的答案。

如果無法使用 SPJ,請保證對精度的要求是有限的,例如:請輸出答案四捨五入後保留小數點後三位的結果。令標準答案為 \(ans\),數據保證對於任意滿足 \(\frac{|x-ans|}{\max(1,ans)}<10^{-9}\)\(x\),四捨五入後結果與 \(ans\) 四捨五入後相同。

可以參考的一些句子:

1
輸入的第一行包含三個正整數 $n$, $m$, $k$ ($1\le n,m\le 2\cdot 10^5$, $1\le k\le 100$) — $n$ 表示數列的長度,$m$ 表示操作個數,$k$ 的意義見題目描述。
1
輸入的第二行包含 $n$ 個非負整數 $a_1,a_2,\ldots,a_n$ ($1\le a_i\le 10^9$) — 題目給出的數列。
1
接下來的 $m$ 行中的第 $i$ 行包含兩個正整數 $l_i$$r_i$ ($1\le l_i\le r_i\le n$),表示第 $i$ 次操作在區間 $[l_i,r_i]$ 上進行。
1
2
3
接下來的 $n-1$ 行,每行包含兩個正整數 $u$$v$ ($1\le u,v\le n$),表示 $u$$v$ 之間由一條邊相連。

數據保證給出的邊能構成一棵樹。
1
輸入的唯一一行包含一個由小寫英文字母構成的非空字符串,其長度不超過 $10^6$
1
輸入的第二行包含一個小數點後不超過三位的實數 $x$ ($-10^6\le x\le 10^6$),意義見題目描述。
1
輸出包含一個實數,當你的輸出與標準答案之間的絕對誤差或相對誤差小於 $10^{-6}$ 時視作正確。
1
2
3
輸出的第二行包含 $n$ 個正整數,表示你構造的一組方案 — 其中第 $i$ 個數表示你打出的第 $i$ 張牌的編號。

如果有多組合法的答案,可以任意輸出其中一組。
在選手代碼內由隨機數生成器生成輸入數據

有的題目會因為輸入數據過大,為了防止讀入用時過長,而要求選手在代碼內通過給定的數據生成器生成數據,代替通過標準輸入或文件輸入來讀入數據。

採用這種做法需要謹慎考慮,因為它有很多缺點:

  • 可能引入了正解所不需要的數據隨機性,或者使得構造數據變得困難
  • 可能增大了理解輸入格式的難度
  • 如果隨機數生成器封裝的不好,可能理解數據生成器本身的使用方法就有難度
  • 如果選手沒有使用出題者推薦的語言,可能需要自己寫一個數據生成器

採用這種做法一般是為了防止讀入數據用時過長,所以一個可能的替代方案是下發一個性能足夠好的 讀入、輸出優化 模板,以儘量保證所有人的讀入用時一致,這樣的話即使讀入用時很久也不會影響不同選手用時的差異。另一個解決方案是將題目包裝成函數調用式(而非 IO 式)交互題,即使算法過程中沒有交互,交互題也可以起到統一讀入用時的作用,IOI 就採用了所有題目都是交互題的方案。但是,這兩種方案都對選手使用的語言有限制,需要出題者手動支持每種允許選手使用的語言。

回到問題的本源,還可以考慮一下過大的輸入數據是否是必要的,有沒有可能使用較小的輸入數據達到目的,以及比正解複雜度稍劣的做法是否有卡掉的必要。

數據範圍

按照 CF 的要求,數據範圍要寫在輸入格式裏,但在國內,數據範圍往往是寫在題目的最後的。

數據範圍中最容易犯的錯誤就是不完整。輸入中的每一個數、每一個字符串都應該有清晰的界定。在上文所給出的輸入輸出格式示例中就有一些數據範圍的正確寫法。

數據範圍的常見遺漏:

  1. 「整數」中的「整」。
  2. 題面中只説了是「整數」沒説是「正整數」,並且數據範圍中只有上限沒有下限。
  3. 字符串沒説字符集。
  4. 實數沒説小數點後位數。
  5. 某些變量沒有給範圍。

你需要保證標程可以通過滿足題面所述數據範圍的 任何一組數據

關於「保證數據隨機生成」

有的題目中會「保證數據隨機生成」,很多時候這樣的限制並不是最優的解決方案,因為「隨機生成」對數據的限制並不明確,會給判斷具體數據範圍、提供 hack 數據帶來困難。

一般來説,「保證數據隨機生成」可以換成解法所需要的數據性質。例如,隨機生成一棵樹往往可以換成限制樹的高度。

如果一定要保證數據隨機生成,應當指定隨機生成的具體操作。例如,生成一棵樹是隨機選擇父親節點還是隨機生成 Prüfer 序列。

需要注意的是,非確定性算法和依賴於數據隨機性的算法是不同的。前者可以對於任意數據都有很高的概率得到正解,而後者是對於大部分的數據能得到正解,對於某些特定的數據則不可能得到正解。

樣例

樣例應當有一定的強度,能夠查出一些簡單的錯誤。讀錯題意的人應當能夠通過樣例發現自己讀錯了題意。

有多種操作的題,每種操作都應在樣例中出現。

有多種輸出的題(如 CF1173A Nauuo and Votes),每種輸出都應在樣例中出現。例外:實際上不可能無解,但要求判斷是否有解的題目。

樣例解釋

題目描述越複雜、越不易理解就越應當有詳細的樣例解釋。

題目難度越簡單就越應當有詳細的樣例解釋。

詳細的樣例解釋可以選擇配上圖片。

較大的樣例可以沒有樣例解釋。

為了照顧色覺障礙者,最好不要使顏色成為理解樣例解釋所必備的。可以用彩色圖片來美化樣例解釋,但如果一定要用顏色傳遞一些必要的信息,最好不要同時出現紅黃或者紅綠。

時限、空間限制與部分分

時限與空間限制的目的是卡掉複雜度錯誤的做法。(當然,也是為了防止評測用時過長,如:只對交互次數有限制而對時間複雜度沒有限制的交互題也有時間限制。)

因此,原則上時間限制應當選取不使錯誤做法通過的儘量大的值。

一般地,時限應滿足以下要求:

  1. 至少為 std 在最壞情況下用時的兩倍。
  2. 如果比賽允許使用 Java,應使 Java 能夠通過。
  3. 不應使錯誤做法通過(實在卡不掉、想放某種錯解過除外)。

為了更好地在放大常數做法過的同時卡掉錯解,一般可以採用同時增大數據範圍和時限的方法。但要注意,有時正解(由於緩存等玄學問題)會在數據範圍增大時有極大的常數增加,此時增大數據範圍不一定能夠增大正解與錯解之間用時的差距。

在有部分分的賽制中,還可以通過設置有梯度的數據、數據範圍稍小的數據來使較為優秀的錯解和大常數正解不能通過,同時使其獲得較高的部分分。

需要注意的是,在數據範圍小於 \(5\cdot 10^5\) 時,應當考慮是否能使用 指令集 通過。

一般情況下空間限制應當設置的足夠大,除非空間複雜度更優的做法的確十分巧妙,值得卡掉空間複雜度大的做法。這種情況下可以考慮設置空間限制較松的部分分。值得注意的是,如果不想卡掉空間消耗較大的做法,數據結構題一般需要設置較大的空間限制。

一道好題應該具有它的選拔性質,具有足夠的區分度。應該至少 4 檔部分分,讓新手可以拿到分,讓高手能夠展示自己的實力。

——vfk《UOJ 精神之源流》

部分分一般分為較小數據範圍與特殊性質兩種。

較小數據範圍一般要設置多檔,即使你想不到某種複雜度的做法,也可以考慮給這種複雜度一檔分。一般來説,為了避免卡常,可以設置一檔極限數據除以二的部分分。

「數據有梯度」最好用多檔部分分替代。

特殊性質部分分的設置要依具體題目而定。理想的特殊性質部分分應當是能夠引導選手思考正解的。與較小數據範圍部分分不同,在你不會針對某種特殊性質的做法時,最好不要給這種特殊性質一檔分。例如:「CTS2019」隨機立方體\(k=1\) 這檔部分分在講題時就被很多人吐槽,稱這檔部分分妨礙了思考正解。

如果題目給分方式與默認方式不同(如:在一般的 OI 賽制比賽中綁 subtask 測試),一定要在題面中説明。

不推薦使用「百分之 XX 的數據滿足 XX」的説法,尤其是數據範圍有多個變量時。例如,「\(30\%\) 的數據滿足 \(n \le 1000\)」和「\(40\%\) 的數據滿足 \(m \le 100\)」可能描述了 \(70\%\) 的數據的性質,也可能只描述了 \(40\%\) 數據的性質。一般來説,subtask 或數據範圍表格是更好的選擇。

造數據

數據生成是出題過程中必要的一步,也是對拍時所必需的,掌握一些生成數據的技巧,就能使造數據的過程更加輕鬆,造出來的數據強度更高。

生成隨機數據

生成隨機數

請參考 隨機函數 頁面。

需要特別提醒的是,在生成值域比隨機函數返回值更大的數時,請 不要 使用 rand() * rand() 之類的寫法,這樣的寫法生成的隨機數非常不均勻。

另外,出題時推薦使用 testlib 來造數據,可以保證在不同平台上同一個種子生成的隨機數相同,並且種子會依據命令行參數自動生成。

生成隨機排列

可以使用 STL 中的 std::shuffle 函數,形如 std::shuffle(a, a + n, rng),這裏 rng 是一個隨機數生成器,比如 std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count())

不要 使用 std::random_shuffle,它在 C++14 中棄用,C++17 中被移除。

生成隨機區間

常見錯誤方法:在 \([1,n]\) 中隨機生成左端點 \(l\),再在 \([l, n]\) 中隨機生成右端點 \(r\)。這樣的話生成的區間會比較靠右。

較為正確的方法(推薦做法):在 \([1, n]\) 中隨機生成兩個數,取較小的作為左端點,較大的作為右端點。

真正均勻隨機的方法:在 \([0, n]\) 中生成一個隨機數 \(x\),若 \(x = 0\),再在 \([1, n]\) 中生成一個隨機數 \(y\),區間為 \([y, y]\);否則按「較為正確的方法」生成。

生成隨機樹

常用方法是為 \(2\sim n\) 的每個節點 \(i\)\([1,i-1]\) 中隨機選擇一個父親。這樣做的話生成的樹不是均勻隨機的,期望高度為 \(O(\log n)\)

還有一種隨機方法:從 \([i\cdot low, i\cdot high]\) 中隨機選擇 \(i\) 的父親。若 \(low\)\(high\) 設置得當,可以造出強度較高的樹。

真正均勻隨機的方法是利用 Prüfer 序列,先生成一個隨機 Prüfer 序列,再通過序列生成樹。這樣做的話,樹的期望高度為 \(O(\sqrt n)\)

除此之外,可以隨機一個排列來給節點重編號/打亂邊的順序。

構造數據

區間相關的題目

常用構造:長度特別小(特殊地,全部為單點)、長度特別大(特殊地,全部為整個序列)。

需要分解因數的題目

可重質因數個數儘量多:\(2\) 的冪。

去重後質因數個數儘量多:最小的若干個質數相乘。

約數儘量多:可以參考 OEIS 上的 A002182 數列。

樹上問題

常用構造:

  • 菊花
  • 完全二叉樹
  • 將完全二叉樹的每個節點替換為一條長為 \(\sqrt n\) 的鏈
  • 菊花上掛一條鏈
  • 鏈上掛一些單點
  • 一棵高度為 \(d\)\(d>1\) 的樹的根節點有兩個兒子,左子樹是一條長為 \(d-1\) 的鏈,右子樹是一棵高度為 \(d-1\) 的這樣的樹。

如果不是在考場上,還可以使用 Tree-Generator 來生成各種各樣的樹。

批量生成數據

筆者推薦使用命令行參數 + bat/sh 的方法。

例如:

gen.cpp:

 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
#include "testlib.h"

using namespace std;

int n, m, k;
vector<int> p;

int main(int argc, char* argv[]) {
  registerGen(argc, argv, 1);

  int i;

  n = atoi(argv[1]);
  m = atoi(argv[2]);
  k = rnd.next(1, n);

  for (i = 1; i <= n; ++i) p.push_back(i);

  shuffle(p.begin(), p.end());
  // 使用 rnd.next() 進行 shuffle

  printf("%d %d %d\n", n, m, k);
  for (i = 0; i < n; ++i) {
    printf("%d%c", p[i], " \n"[i == n - 1]);
    // 把字符串當作數組用,中間空格,末尾換行,是一個造數據時常用的技巧
  }

  return 0;
}

gen_scripts.bat:

1
2
3
4
5
gen 10 10 > 1.in
gen 1 1 > 2.in
gen 100 200 > 3.in
gen 2000 1000 > 4.in
gen 100000 100000 > 5.in

這樣做的好處是,對於不同的數據只需要寫一個 generator,並且可以方便地修改某個測試點的參數。

造數據的要求

數據應當包含各個參數的最小值和最大值。

數據應當包含各種邊角情況。

在使用 subtask 時,數據(包括輸入、輸出)最好覆蓋到值域中的各個範圍,而不是隻有數據範圍的最大值。

為了防止針對特殊構造的特判過掉,可以將不同的構造結合在一個測試點中,或者數據的大部分是構造,摻雜小部分的隨機。

數據中應當包含各種各樣的構造,即使你不知道什麼錯解會掛在這組構造上。(在按測試點給分的賽制中需要酌情處理。)

當然,如果你已知一個(正常人能想的到、寫的出的)正確性有問題的錯解,要儘量卡掉它。

需要特別提醒的是,如果有整型溢出的可能,一定要卡掉會溢出的做法。在有部分分的賽制中,不應使不開 long long 的人得到和暴力一樣甚至更低的分數。

如果有 pretests,pretests 應儘量強,(同時儘量少)。換言之,你需要在 pretests 中(用盡量少的數據組數)包含該題的所有已知叉點。

如果你希望出現少量而非沒有 FST,仍然應當保證 pretests 的強度,因為實際比賽中很可能出現你意想不到的錯誤,導致遠遠高出預期的 FST 數量。

數據的格式

這裏引用 CodeChef 對題目輸入數據的格式要求,可作為一般情況下的參考:

  1. 不使用 Windows 格式的換行符,即 \r\n
  2. 最後一行的末尾有換行符,即整個文件的最後一個字符需要是 \n
  3. 沒有空行。
  4. 任何一行的開頭和末尾都沒有空白字符。
  5. 連續的空格不超過 1 個。

Special Judge

SPJ 編寫教程

輸出方案題和輸出浮點數題是兩種較為常見的需要使用 SPJ 的題型,其它題目視情況也需要使用 SPJ。在 CF 上,所有題目都必須使用基於 testlib 的 checker,例如:題目要求輸出若干個整數時,使用 testlib 自帶的 ncmp checker,選手可以任意輸出空白字符(既可以空格也可以換行)。

checker 一般使用 testlib 編寫。由於 checker 要應對各種各樣的不合法輸出,需要極強的魯棒性,不使用 testlib 是很難寫好 checker 的。

編寫 checker 需要注意以下兩點:

  1. 你需要應對各種不合法的輸出,因此,請檢查讀入的每個變量是否在合法範圍中(readInt(minvalue, maxvalue))。例如:讀入一個在 check 過程中會作為數組下標的變量時必須檢查其範圍,否則可能引發數組越界,有時這會導致 RE,有時則可能判為 AC。
  2. 原則上 checker 中不應檢查空白字符(即,不應使用 readSpace()readEoln()readEof(),值得一提的是,testlib 會自動檢查是否有多餘的輸出)。

題解

題解的目標是讓預計會來參加比賽的人都能看懂。所以官方題解詳細程度的要求會比一般的題解高。

關於部分分

在有部分分的題目中,題解裏可以考慮寫一寫部分分的做法。

關於知識點

解題中用到的知識點應當明確指出。對於一些難度和題目難度相當的知識點,最好給出學習該知識點的資料(比如一篇博客的地址)。

關於定義

題解中不要憑空冒出來一些概念。

例如:dp 的題解要解釋清楚狀態的定義。

關於細節

具體的實現細節如果比較巧妙最好寫出來,否則的話「詳見代碼」也是可以的。如果「詳見代碼」的話,最好在代碼中加上一定的註釋。

標程

標程中最好去掉冗餘部分。比如,有的題解中保留了完整的 define 模板(為了提高做題速度,包含大量 define 與常用函數,常用於 CF 等在線比賽),並且其中很大一部分都沒有用到,這是不好的。

如果涉及到一些題解中沒有詳細説明的實現細節,最好加上適量的註釋。

比賽

比賽通知中的題目難度需真實

Remember that authors tend to underestimate the difficulty of their problems.

——Codeforces PROPOSE A PROBLEM 頁面的提醒

出題人很可能錯誤估計題目的難度,因此,如果要在比賽通知中寫上比賽難度,需要謹慎考慮,最好提前請人來驗題並進行評估。

題目難度的分配

在類國內 OI 的模擬賽中,往往是三道題的整體難度與比賽難度相當即可。

在類 CF/ATC 這種線上賽的比賽中,需要儘量保證難度的遞增(雖然由於對難度的誤估很多時候都並不能真正做到),並且儘量避免出現大的 difficulty gap。可以通過把一題分為難易兩題(兩個 subtask)來減少 difficulty gap,但是分 subtask 需要謹慎考慮,也有很多人不喜歡 CF 賽制中的 subtask(Are subtasks evil?),原因包括但不限於:

  • 由於賽制原因,可能先做 easy version 再做 hard version 罰時更少而總分更高
  • subtask 的賦分往往與題目難度不成正比
  • 很多時候 easy version 的題目並不是一道合格的題目(不有趣)
  • 很多時候 easy version 的解法對於思考 hard version 的正解沒有幫助

題目知識點的分配

一場比賽應儘量涵蓋較廣的知識點(專題訓練賽當然除外)。

經典反例:涵蓋了動態規劃、期望、組合計數、容斥原理、多項式等多種知識點的 CTS2019。

我要從五道題裏選六道,我也很無奈啊。

——CTS2019 組題人給出的理由,沒有收到足夠多的題目投稿

出題平台

Polygon

Polygon 是一個功能非常強大的多人合作出題平台,可以作為在任何網站(使用 package 功能導出到不支持 Polygon 的網站)多人合作出題的首選方案,單人出題(尤其是在不同設備上出題)時也是很不錯的選擇,使用方法參見 Polygon 簡介

Codeforces

Codeforces 是全球最著名的算法競賽網站之一,題目質量較高,非常適合有一定出題經驗並且想進一步提升出題水平、想要出一套高質量題目的出題人。不足之處是審核速度較慢(一般要幾個月),但你也可以在審核期間就開始題目的準備(雖然有題目被否掉導致準備白費了的風險)。

出題資格

  • 藍名且參加過至少 25 場 rated 比賽;
  • 紫名且參加過至少 15 場 rated 比賽;
  • 橙名且參加過至少 5 場 rated 比賽;
  • 紅名或黑紅名。

提交比賽申請

有了出題資格後,在側邊欄可以看到 Propose a contest/problems 按鈕。

點進去之後,先寫一份 contest proposal(在 PROPOSE A CONTEST 裏寫),然後再寫 problem proposal 並添加進比賽裏。

題目決定好之後,就可以將 contest proposal open to review(提交審核)了。

在 Polygon 上準備題目

參考 Polygon 簡介

與管理之間的聯繫

與管理聯繫有兩個作用:

  1. 加快審核速度。
  2. 進入準備階段後管理會提供建議和幫助。

正規的聯繫方式是在 proposal system 中以 proposal 的形式提交申請,管理開始審核之後以 comment 的形式在 proposal 的下方進行討論。

實際上,如果 proposal 長時間沒有過審,可以考慮私信聯繫管理(其實 CF 上寫了 "Don't send private messages or emails to coordinators",但 300iq 在 評論 中表示可以私信他)。

Comet OJ

Comet OJ 鏈接

已經不再活躍(截至 2021 年 11 月,最後一場比賽是 2020 年 1 月的)。

出題申請:https://info.cometoj.com/contests/Questionnaire_IssuerInfo/

CodeChef

印度的算法競賽平台,有三種賽制:10 天且帶 challenge 的 Long Challenge,2.5h 類 ICPC 的 Cook-Off,3h 類 IOI 的 LunchTime。

出題 FAQ:https://www.codechef.com/wiki/faq-problem-setters

出題指南:https://www.codechef.com/problemsetting

AtCoder

日本的算法競賽平台,出題聯繫方式:contest@atcoder.jp

UOJ & LOJ

比賽不多的國內 OJ。

洛谷

個人公開賽

在「我的題庫」中出題並提交比賽申請。

團隊公開賽

在團隊頁面中出題並提交比賽申請。

參考資料

  1. vfk《UOJ 精神之源流》

  2. 王天懿《論偏題的危害》

  3. CF 出題人須知國內可訪問的圖片版

  4. CF 出題人的自我修養

本文由作者本人自 ouuan 的出題規範 搬運而來並有所修改、補充。