###### tags: `tutorials` `polygon` `CompetitiveProgramming` `codeforces` # Polygon使用指北 進到[Polygon](https://polygon.codeforces.com/)後,請先註冊帳號,可以和CF用相同的ID或密碼XD ![](https://i.imgur.com/qGutai7.png) 要新增題目請點選 **New Problem**,這時候填的名稱是後台看到的,可以取個簡短的英文名稱以供識別。 點選 **Start** 來編輯問題,修改已經存在的題目則是點進 **View Problem**後點選 **continue**。 --- ## General Info 這邊可以修改的資訊包含 * I/O來源(預設是$\texttt{ stdin, stdout}$) * 時間限制、記憶體限制(預設是1000ms、256MB) * 是否為互動題 * 是否要自己配分(如果要**部份分**請打勾) * 題目的tag(賽中不會顯示) 如果想改變剛剛取的名稱可以點進Advanced更改。 --- ## Checker Checker用來比對參賽者和出題者的答案,在CF常常看到的checker幾乎都可以從選單選取,包含容許誤差的浮點數比對、大小寫不敏感的yes/no等等 一般整數或字串比對基本上選 ==**wcmp**== 就對了。 特殊題必須 [自己寫Checker](#Checker1) > < --- ## Solution Files 沒什麼好說的,點選 ==**Add Solutions**== 把你的官解丟上去就好。 在Polygon上解答以Main Correct Solution的輸出答案為準,自然必須保證官解不會RE、TLE或MLE。 --- ## Statement 這邊就是參賽者看得到的部分了,請選擇 ==**English**== 建立題敘(如同各位所知,Codeforces只有English和Russian,其他語言不會被Codeforces讀到) | Field | Desciption | | ------------- | ---------- | | Encoding | 呃...就是題敘的編碼,使用預設的UTF-8就可以了 | | Name | 會顯示出來的題目名稱,例如 *Tokitsukaze and Mahjong* | | Legend | 主要題敘 | | Input Format | 輸入格式 | | Output Format | 輸出格式 | | Notes | 提示、範測說明等通常放這 | | Scoring | 若有配分會放這(必須先打勾左下角**Show section 'Scoring'**) | | Tutorial | 題解,比賽裡面看不到 | 注意每個欄位都是$\TeX{}$格式,例如%必須加上反斜線才能正常顯示;此外雖然勾選的是英文,**但還是可以使用中文**。 請盡量使用$\TeX{}$描述,並請記得 ==**標明變數範圍**==。 ![](https://i.imgur.com/QGaSRyC.png) 每次更新完都要記得按 **Save** ,如果想預覽可以選擇 **In HTML**。 --- ## Tests 題目的測資,是最麻煩的部分...點選 **Add Tests** 後有兩種方式產生測資 * 在本機上用檔案輸出的方式生出txt檔並選擇from the file上傳,若要一次上傳很多個可以壓縮成 **zip** 檔並選擇from the archive上傳 * 寫 [**generator**](#Generator) ,上傳到Files的地方,並用script雲端(?)生測資。 ![](https://i.imgur.com/QCII90h.png) 點選 **Preview Tests** 可以讓所有Test都跑過一遍Validator和Main Correct Solution,看看有沒有任何地方是出錯的。 要一次更改多個測資的設定可以選取起來一起更改,右邊打勾選取可以使用shift、ctrl等快速選取,注意更改設定時若不是點選表頭則不會影響到所有選取的測資。 將測資設為 **Example** 即是範測,會顯示於題敘下方。 別忘了 ==**準備範測**== ! ### Setting Subtasks 勾選左上角的 **Enable Groups** 可以將測資分組,以調整Points Policy、Feedback Policy、Dependencies等等。 * #### Points Policy EACH_TEST會將該組所有通過的配分加總,而COMPLETE_GROUP則必須通過該組所有測資才可得到該組所有分數。 * #### Feedback Policy 這是讓參賽者看到的得分情形,POINTS僅顯示該題總分,COMPLETE則顯示每個測資所得到的分數,ICPC和COMPLETE相似,但若沒通過某個測資則會直接跳到下一個組別。 * #### Dependencies 組別之間的依賴關係,必須通過某一些組別才會去執行這個組別的測資。 通常將Points Policy設為 ==**COMPLETE_GROUP**==,而Feedback Policy設為 ==**ICPC**== 如此每個組別僅需指定一個分數,每組隨意選擇一題並點選Points即可更改配分。 **有Subtask一定要記得配分!配分之前必須在General Info處勾選允許配分** --- ## Validator Validator是拿來檢查測資的格式是否正確的,通常只能自己寫(但如果沒有要讓人hack也可以姑且不寫XD) [說明與範例](#Validator1) --- ## Invocation 這邊可以把Test餵進Solution裡並查看執行狀況(執行時間、記憶體用量等),也就是測試執行 橘色代表你的Solution跑超過時限的一半(暗示你時限要開官解時間的兩倍) --- ## Package > 萬事俱備,只欠commit 點選右下角 ==**Commit Changes**== 確認所有修改(記得把寄mail通知你的選項取消XD) 然後就可以Create Package了,通常選擇 ==**Standard**== 接著大概要等個一分鐘讓他跑,~~這是最累也是最輕鬆的環節~~ --- ## Manage Access 終於把整個問題都包好啦,現在就剩下丟到Codeforces上面去了 點選 **Add Users** 並輸入Codeforces給他Read的權限,接著複製右邊的連結 ![](https://i.imgur.com/FiqrVC5.png) 你可以在Codeforces隨意開一場比賽,在Add Problems時把剛剛的網址貼上去就可以看到題目啦! --- --- # More About Judgement 這裡簡單介紹一些評測程式的寫法。 ~~其實只是中文翻譯啦QAQ~~ [原文連結](https://codeforces.com/testlib) --- ## testlib.h 首先要 `include "testlib.h"` ,這是一個放在 Polygon(?) 上的函式庫,可以協助你撰寫generator、checker、validator、interactor 等等。 --- ## Generator <!-- 電 --> generator的實作十分簡單,將你要生成的測資輸出到 $\texttt{stdout}$ 即可。 以下是一個generator的範例,用以生成兩個在 $[1,n]$ 的整數。 ```cpp=1 #include "testlib.h" #include <iostream> using namespace std; int main(int argc, char* argv[]){ registerGen(argc, argv, 1); int n = atoi(argv[1]); cout << rnd.next(1, n) << " "; cout << rnd.next(1, n) << endl; } ``` 一些亂數的用法: |Method|Description| |-|-| |`argv[]`|呼叫generator可以使用的參數, `argv[0]`是程式本身, `argv[1]`開始就是用script呼叫generator的輸入參數。| |`registerGen()`|初始化generator,才可以使用`mt_19937`(?)的`rnd` 它會依據你傳進去的`argv[]`做hash,`1`是version1| |`rnd.next(n)`|等機率生成 $[0,n)$ 的隨機數,若傳入整數,則random的結果是整數; 若傳入浮點數,則random結果是浮點數。| |`rnd.next(a,b)`|等機率生成 $[a,b]$ 的隨機數| |`rnd.next("qlig|njis|suum")`|從`"qlig"`、 `"njis"`、`"suum"` 隨機挑一個生成| |`rnd.next("[a-z]{10}")`|生成長度$10$的小寫英文字串,括號內可以塞任意 [regular expression](https://en.wikipedia.org/wiki/Regular_expression) [(正規表示式)](https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F)| |`rnd.any(container)`|從容器裡等機率隨機 挑選一個元素,支援`std::vector`與`std::string`| |`shuffle(l,r)`|隨機改變$[l,r)$中元素的順序| ### Generate tests using Script 使用generator的方法如下: 1. 將generator上傳至Polygon的Files 2. 點到tests,底下有個script框框 3. 輸入script,點選Save Script,generator將會自動生成測資 <!-- 147! --> script的語法範例: ```line=1 <#assign maxN = 1000/> <#list 1..20 as testNumber> generator ${maxN} 200 100 ${testNumber} > $ </#list> <#assign maxN = 100000/> <#list 1 .. 20 as testNumber> generator ${maxN} 200 100 ${testNumber} > $ </#list> ``` <!-- 睏8888888888888 --> |Method|Description| |-|-| |`<#assign>`|設定變數值,變數不用先宣告| |`<#list a..b as name>`|迴圈語法,左閉右閉| |`gen [params] > test`|生成測資,`gen`是你generator的檔名, `params`就是generator的引數`argv[]`,以空白隔開, `test`是你要將generator的輸出放在第幾筆測資, `$`會自動挑最小未使用的數字。 **每筆測資的`params`必須不能完全一樣**| --- ## Validator 如果題目要可以hack,或者檢查generator的測資是否符合題目限制,那麼可以使用vaildator來檢查。 checker只有一個輸入來源`inf`,如果validator在執行時測資沒有符合validator需要的格式,那麼validator就會報錯。 以下是validator的簡單範例,題目是將兩個不大於$10^5$的正整數相加: ```cpp=1 #include "testlib.h" int main(int argc, char* argv[]) { registerValidation(argc, argv); int a = inf.readInt(1, 100000, "a"); inf.readSpace(); int b = inf.readInt(1, 100000, "b"); inf.readEoln(); inf.readEof(); } ``` `inf`的這些函數對應的需求格式如下: |Method|Description| |-|-| |`registerValidation()`|讓validator可以使用`inf`輸入串流| |`int readInt()`|輸入一個 int 型別的整數| |`long long readLong()`|輸入一個 long long 型別的整數| |`double readDouble()`|輸入一個浮點數| |`char readChar()`|輸入一個字元| |`string readWord()`|輸入一個不包含空白、換行、tab的字串| |`string readLine()`|輸入一整行(包含換行字元)於一個字串| |`char readSpace()`|輸入一個空白字元(`' '`)| |`void readEoln()`|輸入一個換行字元(`'\n'`)| |`void readEof()`|確定測資結尾| ### Range and Constriants 這些函數還可以限制輸入的範圍: |Method|Description| |-|-| |`int readInt(l,r)`|輸入一個界在 $[l, r]$ 的 int 型別整數| |`vector<int> readInts(n,l,r)`|輸入$n$個界在 $[l, r]$ 的 int 型別整數(以空白隔開)| |`long long readLong(l,r)`|輸入一個界在 $[l, r]$ long long 型別整數| |`vector<long long> readLongs(n,l,r)`|輸入$n$個界在 $[l, r]$ 的 long long 型別整數(以空白隔開)| |`double readDouble(l,r)`|輸入一個界在 $[l, r]$ 的浮點數| |`double readStrictDouble(l,r,m,M)`|輸入一個界在 $[l, r]$ 的浮點數,其小數點後的位數必須界在$[m, M]$之間| |`char readChar(c)`|輸入一個字元,但這個字元必須等於`c`| |`string readWord(regex)`|輸入一個不包含空白、換行、tab的字串,這個字串必須符合傳入的 regular expression| |`string readLine(regex)`|輸入一整行(包含換行字元)於一個字串,這個字串必須符合傳入的 regular expression| 若`inf`的輸入超出範圍,validator就會報錯,可能就必須重寫generator或validator。 ### Parameter *variableName* `readInt/readLong/readDouble/readWord/readLine` 這些函數可以在所有引數之後再加入一個字串`variableName`來當作錯誤訊息中回傳值的變數名稱,方便人類使用者閱讀錯誤訊息。 ```cpp=1 a = inf.readInt(1, 10, "a"); // 若此時 generator 的測資不小心輸出 0,則 validator 會輸出錯誤訊息: // FAIL Integer parameter [name=a] equals to 0, violates the range [1, 10] ``` ### ensure/ensuref `ensure`函數可以檢查一個條件是否成立,例如要確保測資兩個數字不相同,可以寫`ensure(a != b)`,如果測資違反了這個條件,則會在錯誤訊息看到`FAIL Condition failed: "a != b"`。 `ensuref`是帶有自訂錯誤訊息的`ensure`,例如`ensuref(a != b, "Two integers can't be the same")`。 **注意:validator必須照著題目敘述寫,切勿照generator寫,不然validator將會完全沒有作用!** --- ## Checker 假設有多組正確答案,但只要輸出其中一組時,就必須用到自己寫的checker。 checker有三個輸入來源,分別是`inf`、`ouf` 與 `ans`。三個輸入來源都可以用`inf`有的那些格式檢查輸入函數。 |Stream|Description| |-|-| |`inf`|測資,也就是手動或generator生成的資料。| |`ouf`|待評測的code對於這筆測資的輸出。| |`ans`|正解對於這筆測資的輸出。| 這是一個簡單的a+b問題的checker範例: ```cpp=1 #include "testlib.h" int main(int argc, char* argv[]) { registerTestlibCmd(argc, argv); // 這裡 checker 不需要用到 inf (測資) 的資料 int pans = ouf.readInt(2, 200000, "sum of numbers"); int jans = ans.readInt(); if (pans == jans) quitf(_ok, "The sum is correct."); else quitf(_wa, "The sum is wrong: expected = %d, found = %d", jans, pans); } ``` `registerTestlibCmd()`是checker的初始化函數,這裡不加著墨。 當然,如果checker在執行中,遇到不符合格式的輸入,待測程式將會得到一個 `_pe`,在 Codeforce 上會顯示 Wrong Answer。 以下是幾個可以用的評測結果: |Verdict|Description| |-|-| |`_ok`|**<p style="color: green; disply:inline ; font-size:24px">AC</p>**| |`_wa`|**<p style="color: red; font-size: 24px">WA</p>**| |`_pe`|輸出格式錯誤| |`_pc(score)`|部分分數,得分會是`score`+16,不過IOI制好像不能用這個功能| |`_fail`|官解爛掉了 QAQ (解題者的輸出比官解好)| 這些評測結果可以用`quitf(VERDICT, "comment", ...)` 回傳給Codeforces, 也可以用`quitp(_pc(score), "comment", ...)`回傳partial score。 剩下如`_tl`、`_re` 等等可能的評測結果,Codeforces會自動依據使用時間以及記憶體來判斷,不需要依靠checker。 --- ## Interactor 互動題大概是Polygon裡面最難搞的東西,因為它的輸出/輸入串流非常多,除了`inf`、`ouf`之外,還多出了$\texttt{stdout}$與`tout`可以用,而且功能與用法與一般題目有些微的差距。 |Stream|Description| |-|-| |`inf`|與一般題目一樣,都是輸入測資,不過在互動題中只有interactor、validator與checker可以獲得測資,待測程式與正確答案只能透過interactor獲得線索| |`ouf`|接受待測程式(或正確答案)的輸出,因為正確答案也要與interactor互動,所以interactor沒有`ans`串流| |$\texttt{stdout}$|在互動題中,待測程式(或正確答案)無法直接獲得測資,也就是說待測程式的輸入來源只有interactor的$\texttt{stdout}$| |`tout`|輸出方法是`tout << n << endl;`,待interactor執行結束後,與待測程式互動`tout`的內容會作為checker的`ouf`輸入,與正確答案互動`tout`的內容會作為checker的`ans`輸入| 當然,interactor執行中也可以使用`quitf`、`ensure`等函數,interactor會立即中止且回報評測結果。 以下是interactor的範例,題目是猜數字: ```cpp=1 int main(int argc, char* argv[]){ registerInteraction(argc, argv); int n = inf.readInt(); // 要猜的數字 (只有interactor知道) cout.flush(); // 確定緩衝區沒有其它輸入卡住 int left = 50; bool found = false; while(left > 0 && !found){ left--; int a = ouf.readInt(1, 1000000000); // 待測程式的猜測 if(a < n) cout << "too small" << endl; if(a > n) cout << "too large" << endl; if(a == n) cout << "correct" << endl, found = true; cout.flush(); } ouf.readEof(); tout << found << ' ' << left << endl; } ``` ### Checkers in interactive problems 互動題的checker可以使用`ouf`與`ans`獲得interactor的`tout`內容,以及使用`inf`取得原始的測資內容。 checker的角色是比對interactor對於待測程式的回報(猜測次數之類的),別忘了正確答案也有可能猜太多次,所以記得將`ans`的輸入結果與`ouf`結果做同等對待,才是好checker喔! 上述題目的interactor可以搭配以下checker使用: ```cpp=1 #include "testlib.h" int main(int argc, char* argv[]) { registerTestlibCmd(argc, argv); int pfound = ouf.readInt(), pleft = ouf.readInt(); int jfound = ans.readInt(), jleft = ans.readInt(); if(!jfound) quitf(_fail, "main solution couldn't guess the number"); if(!pfound) quitf(_wa, "couldn't guess the number with 50 questions"); quitf(_ok, "guessed the number with %d questions!", 50 - pleft); } ```