1. 程式人生 > 實用技巧 >JOISC2020 划水記&題解(施工中)

JOISC2020 划水記&題解(施工中)

\(Part1\) 遊記

咕咕咕

\(Part2\) \(solutions\)

不會的/沒寫完的先鴿著,補題之後再補

目前只寫了 \(D1T1\) \(D2T2\) \(D3T3\) \(D4T1\)

\(solutions\) \(for\) \(Day1\)


\(D1T1\)

$description : $ LOJ連結

$solution : $

首先有一個\(O(n^2)\)的樸素\(dp:\)

\(f[i][j][0/1]\)表示考慮到前\(i+j\)個數\(,\)選了\(i\)\(A\)\(j\)\(B,\)\(i+j\)個數選的是\(A/B,\)是否可行\(.\)

這個\(dp\)

的期望得分是\(12\)\(.\)

一種想法

一種想法是考慮構造出一個合法的方案\(.\)

\(f[i][0/1][0/1]\) 表示在考慮序列的前\(i\)\(,\)\(i\)個選的是\(A/B,\)最多的\(A/B\)的數量是多少\(.\)

考慮從尾到頭構造方案\(.\) 對每個位置列舉這個位置用\(A\)還是用\(B,\)

記當前位置為 \(now,\) 當前位置的字元為 \(c,\) 已經用了 \(cntA\)\(A\)\(cntB\)\(B.\)

那麼方案合法的條件就是 \(cntA\) \(+\) \(f[now][c][0] \geq n\)\(cntB + f[now][c][0] \geq n.\)

按這種方法構造方案即可 \(.\) 複雜度 \(O(n).\)

評測連結

"一種想法"的正確性證明\(:\) \((by\) \(czynt\) \()\)

如果存在 \(max({a_i,b_i})>max({a_{i+1},b_{i+1})}\)或者\(min({a_i,b_i}) < min({a_{i+1},b_{i+1}}),\)就沒得選擇\(,\)不用考慮他\(.\)

現在考慮\(,\)如果\(max({a_i,b_i}) \leq min({a_{i+1},b_{i+1}}),\)那就是分開的\(;\)

你用這個可以把它分成若干段\(;\)

現在問題就是每一段裡面可不可以任意分\(.\)

我們發現你可以在任意時候跳到比較高的上面去\(,\)就行了\(.\)

\(D1T2\)

$description : $ LOJ連結

$solution : $

一種亂搞

把所有的矩形 cpp random_shuffle() 一下\(,\)然後把前\(k\)個矩形找出來作為起始矩形\(,\)後面的\(n-k\)個矩形用貪心的方法決策和\(k\)個矩形中的哪一個求交\(.\)

這個做法可以過掉賽時的資料\(.\) 然而賽後\(uoj\)上就被\(hack\)

LOJ評測連結 UOJ過不去hack資料的評測連結

\(solutions\) \(for\) \(Day2\)


\(D2T2\)

$description : $ LOJ連結

$solution : $

顯然題意相當於我們可以一邊加入邊\((A_i,B_i)\)一邊進行題目中的操作\(.\)

我們把同時存在邊\((u,v)\)\((v,u)\)的點對合並起來\(,\)縮成一個塊\(.\)

那麼一條兩個塊之間的邊\((cur_x,cur_y)\)對答案的貢獻為\(size_{cur_y}\)

一個大小為\(s\)的聯通塊對答案的貢獻為\(s*(s-1)\)

在聯通塊之間出現可合併的點對時合併聯通塊\(,\)並把某一個聯通塊的所有邊都合併到另一個聯通塊上\(.\)

可以用啟發式合併解決\(.\)複雜度\(O(mlog^2m)\)

評測連結(高度壓行預警)

\(solutions\) \(for\) \(Day3\)


\(D3T3\)

$description : $ UOJ連結

$solution : $

這題可以分為兩個不同的子任務\(:\)一是\(A=3,B=0,m>=n-1,\)另一個是\(A=2,B=6,m=n-1\)


\(case1:\) \(A=3,B=0,m>=n-1\)

這個子任務的含義\(,\)就是說我有三種標記\(,\)在任意圖的情況下每一步都必須沿著最短路走\(.\)

以下為了方便\(,\)把標記看成顏色\(,\)不同的標記即不同的顏色\(.\)

我們考慮以終點\(0\)為根對給定的圖進行\(BFS.\)\(dis_i\)\(0->i\)的最短路\(.\)

\(BFS\)之後\(,\)對於圖中的一條邊 $ (u,v) $ 一定有 \(|dis_u-dis_v] \leq 1\)

換言之\(,\)我們\(BFS\)後的圖就是有若干"層"點\((\) 我們把\(dis_x\)相同的點看做一層 \(),\)其中層與層之間有連邊\(,\)同時一層點的內部也有相互連邊\(.\)

那麼我們需要保證\(,\)對於每個點我們必須能夠讓它能判斷下一步往哪個標記走\(,\)
並且保證下一步一定走向下一層點\(,(\)\(dis_x\)更小的點\(,\)也就是說\(,\)不能走到\(dis\)更大的點\(,\)也不能通過這一層內部的邊走到\(dis\)相同的點\().\)

所以我們就可以知道\(:\)

\(1.\)每一層的點與下一層的所有連邊應該是同種顏色\(;\)

\(2.\)每一層內部的連邊的顏色應當和前一層連線到這一層的邊顏色相同\(.\)

最後還剩下一個問題\(:\)對於一個點\(,\)
我可能會在int Move(vector<int> y) \(get\)到兩種可能的顏色\(,\)但我要怎麼\(check\)哪一種顏色是我這一步需要走的呢\(?\)

有一個(可能)比較精妙的構造 \(:\) 我們可以給顏色設計一個類似石頭剪刀布的優先順序 \((\) 比如 \(,\) \(0>1,1>2,2>0\) \().\)

這樣我們就可以解決這個\(case\)\(.\)

關鍵程式碼\(:\)

//Anthony 比較長,就不貼了,評測連結裡有程式碼
//Catherine
namespace subtask0{
	int A,B;
	int work(std::vector<int>y){
		int i,s = 0,a = -1,b = -1;
		for (i = 0; i < A; ++i) if (y[i]){ if (a == -1) a = i; else b = i; }
		if (b == -1) return a;
		//這裡的a和b是和當前點相鄰的邊的兩種顏色
		if (a > b) swap(a,b);
		if (!a && b == 1) return 0;
		if (a == 1 && b == 2) return 1;
		if (!a && b == 2) return 2;
	}
}

\(case2\) \(:\) \(A=2,B=6,m=n-1\)

這個子任務呢\(,\)就是要求我們給一棵以 \(0\) 為根的樹上黑白染色\(,\)然後通過不超過 \(dis+6\) 步能成功走到點 \(0\) \(.\)

由於是一棵樹\(,\)所以對於一個節點\(x,\) \(x\)的父節點只有一個\(,\)而子節點可以有一個或多個\(.\)

如果說一個點的 \(u\) 度數 \(>2\) \((\) 換言之 \(,\) \(u\) 有不止一個子節點 \(),\)

那麼我們就必須 \(u\) 與兒子之間的所有邊 設成同一個顏色 \(,\) 且和 $ $ \(u\) 與父親之間的邊 顏色不同 \(.\)

這樣的話\(,\)當我走到的點\(degree>=3\)\(degree = 1\)\(,\)我就能夠簡單的判斷方向 \(,\) 知道下一步應該走哪裡 \(.\)

問題來了 \(!\) 如果有一條直上直下的長鏈\(,\)鏈上所有點的度數都是 \(2,\) 怎麼辦啊 \(?\)

因為度數為 \(2,\) 而根據上述的連邊方式是不能在 \(6\)次錯誤之內 判斷方向的\(,\)所以我們要對現有的方案進行一些修改 \(.\)

具體來說\(,\)我們是要對鏈進行一些修改\(,\)使得在鏈上可以快速判斷方向\(.\)

通過一些人類智慧\(,\)我們有了一個字串\(010011.\)

這個字串有著很好的性質\(:\)我們一旦\(get\)到了他的一個迴圈同構串\(,\)就可以直接判斷是正向還是反向\(.\)

這個字串怎麼用呢\(……\)

我們把這個串的迴圈掛在鏈上 \(,\)\(010011010011...\)\(100110100110...\)

因為\(B=6\) \(,\) 所以我們只能向錯誤的方向最多走 \(3\)\(.\)

但是我們發現走了三步之後正好能\(get\)到串的 \(5\) 個字元 \(,\) 那麼這個串的一個迴圈同構串也就被唯一確定了 \(.\)

至此我們能夠在 \(3\) 步之內正確判斷一條鏈上的方向 \(.\) 如果中途碰到度數 \(= 1\) \(or\) \(\geq 3\) 的點我們就可以直接\(check\)\(.\)

那麼這個 \(case\) 也被我們解決了 \(.\)

//這個case程式碼量較大,不適合貼在部落格裡,但是評測連結裡有程式碼

評測連結

\(solutions\) \(for\) \(Day4\)


\(D4T1\)

$description : $ 目前只有\(atcoder\)上有\(,\) \(loj/uoj\) 上都沒有 \(,\) 就不掛連結了

$solution : $


一種十分暴力但是非常難寫的做法

我們考慮對於每種顏色強行求出如果要包含它\(,\)需要的顏色種類有多少種\(.\)

對每種顏色建出虛樹\(.\)然後如果顏色\(c\)包含了虛樹上的一個點\(,\)那麼就向那個點所在的顏色連邊\(.\)

最後\(,\)如果顏色\(u\)在圖上能走到另一個顏色\(v,\)說明顏色\(u\)需要包含的顏色集合中有\(v.\) 那麼我們只要求出縮點之後的圖就可以解決問題了\(.\)

現在我們有\(O(n)\)個 一個點 往 樹上的一條鏈 連邊的操作\(.\)考慮優化建圖\(.\)

樹剖\(,\) \(st\) 表優化建圖 \(,\) 最後 \(tarjan\) 縮點解決\(.\)

時空複雜度為\(O(nlogn),\)程式碼長度很長 \((\) \(5\)\(K\) \()\) \(,\) 空間很緊\(.\)

評測連結1(空間500MB)

評測連結2(空間487MB)


一種簡易方便的做法

咕咕咕