NOIP 2024 遊記 & 賽前訓練總結

dengchengyu發表於2024-11-28

NOIP 2024 遊記 & 賽前訓練總結

前面都是比賽前的訓練,會含有一些比賽經驗。遊記寫在最後。

day #-18(11.11)

賽時

今天做信友錯的模擬賽。

第一題是和最短路有關的,看到 \(n\le 500\) 就想到了 \(n^3\log n\),然而看了很久都不會做,於是果斷火速打了 \(O(n^4)\) 的暴力走人,get 50pts。

然後看第二題,發現是最大異或路徑,正好最近剛學了線性基,於是想到之前做的一道線性基的題目,把每個環加入線性基。然後就能做到 \(n^2\),再補一個樹的性質。get 55pts。此時比賽剛過一小時。

很快啊,其他人還在做 T1,我就開了 T3。看了一會想到用並查集維護,再記錄修改前的以實現撤銷。發現大樣例都過了,此時我還以為複雜度是正確的,此時比賽剛剛過半。但是去了一下洗手間,就想到可以構造一條鏈卡到平方,這個做法只能過隨機的性質和不撤銷的性質。於是又補了鏈的性質。get 55pts。

此時光靠暴力已經得到 160pts 了。

比賽剛過半,看了一下第四題,覺得還是要先補一下第一題的性質,補了一個性質 A,多得了 10pts。

這時我去嘗試想中間兩道題的正解,無獲,於是開始打最後一題的暴力。

後半程有點鬆弛。

發現暴力分很多啊,在最後 20 分鐘寫完了 \(n\le 9\) 的 40pts 暴力,然後又補了前兩個性質共 8pts。

賽後 & 總結

然後——比賽結束,最後一題 48pts \(\to\) 60pts,最後總分 \(50+55+55+60=230\) 喜提總榜 14,jz 第 3,原來是暴力場。

賽後發現 T1 是線段樹分治的思想,還有 Floyd 插入單個點的技巧,我們只以一個點集內的點為中轉點轉移(插入順序沒有要求),就可以求出只經過(不包括起點終點)這個點集內的點的最短路。

T2 是線性基的結論,\(qmax(i\oplus j)=qmax(i)\oplus qmin(j)\)

T3 是樹剖+線段樹的 4K 大資料結構,就是把輕鏈的貢獻全部掛在重鏈上,有點類似動態 DP 那種。

T4 是論文題,用結論的式子計數。而暴力就是遞迴,然後在遞迴過程中進行剪枝就能跑得比較快。

總結:這場比賽時間分配還不錯,前面暴力打得比較快,除錯也沒花多少時間,大概是經驗越來越豐富了吧。但可惜後面充裕的時間內沒有想出正解,其實正解是不難的,還有提升空間。

改題經驗

改 T3 的時候有一個陣列沒開 long long,幸好很快就查出來了。

改 T1 的時候在分治時把列舉 \([l,mid]\) 寫成 \([1,mid]\) 了。

day #-14(11.15)

今天輸了!

20:35 開始打 CF div.2。

順序開題。

T1 是唐題,T2 是唐題。

T3 我蠢了,1 h 時還沒過,於是去寫 T4。

T4 也想了不少時間,終於想到一個用 set+BIT 的做法。

T5 是樹同構,我還以為要換根於是沒寫。

T6 是互動,沒看。

最後成果:T1+T2+T4。

賽後發現 T3 的正解就是我想的那樣,結果 tm 的 T5 的同構是有根樹下的,又被騙了。

譴責 CF 的題面跟 shi 一樣長。

總結

  • 要有耐心,不要因為前面的題不會,後面就不去想了。
  • 不要花太多時間在一道題目上,扔掉後就不要再去想它了。

day #-13(11.16)

今天又做心有錯。

T1 簡單題。

後面都不會做,把暴力都拿了。

預計:100+60+60+50=260。

實際:72+60+60+20=212。

T1 我自信地認為 \(n^3\times V\) 不會爆 long long,導致我沒有取模。

而 T4 不知道為什麼 \(O(n^3)\) 沒有拿到第二檔分,tm 的 OI 賽制還綁 subtask。

賽後

T2 是對於長度相同的區間的線段樹是同構的,運用這個性質記憶化遞迴

T3 題面很簡單,正解是神奇雜湊,被騙啦,哈哈哈,把 shi 題放在 T3 讓人以為是科技題。大概是把 \(n\) 個位置用雜湊值轉化成 \(\sum\) 個字元的不同情況。

T4 是分治,矩形分為跨過分界線和不跨過兩種,不跨過是子問題,遞迴求解;跨過可以暴力做。每次對當前矩形割長的一邊,這樣複雜度就正確了,時間是 \(T(n)=4T(n/2)+O(n^2)=n^2\log n\)

總結

  • 要有耐心,要充分利用時間。
  • 要注意資料範圍,記得取模,不要想當然。
  • 神奇的題要想想神奇的演算法,比如雜湊之類的,這又讓我想起星戰那道奇葩題。

dsy #-9(11.20)

心有錯。

賽時

T1 想到二分答案,然後就不會做了,我都要以為是罰坐場了。

前面浪費了一個小時,然後去開 T2,寫了一個 \(O(n^2)\) 暴力,對著邊權討論一下,然後終於想到了用線段樹模擬最短路的過程,寫完已經十一點了。

後面的時間打了剩下三題的暴力,這時感覺 T4 有點像雜湊,但是覺得敲不出來了,就沒繼續想。

賽後

發現 T1 是我不會證的結論(猜結論題),T3 是 SG 函式的規律(打表題),T4 真的是雜湊。

之後新學了博弈論相關的知識。

總結

  • 這場比賽在第一題浪費太多時間了,應該猜到題目不按難度排列的,一定要先做擅長的題和有想法的題。

day #-4(11.25)

今天的題都挺好的,但就是我沒做出來幾道,感覺思維不夠敏捷了?這一週一定要努力了。

今天還是做心有錯,T1 浪費了我一個半小時,T2 已經接近正解了,T3 用了錯解結果過了,T4 其實不算難題但我沒看。

T1

TAG:線性篩、質因數分解、最最佳化分解。

這道題很容易想到 \(O(T\dfrac{\sqrt[k]n}{\ln\sqrt[k]n})\) 的做法,就是當 \(k=3\) 時,質數分解到 \(2^{20}\),之後列舉所有質數即可。

然後我就絞盡腦汁沒想到正解,但我想到和正解很接近的思路:
發現有很多小的質數對答案沒有貢獻,那怎麼去掉它們呢?

然後我並不知道怎麼去掉它們,原來只需要把 \(\sqrt[k+1]n\) 的質數篩掉,剩下的最多是三個質數相乘,這個時候直接把剩下的 \(n\) 開根即可,時間複雜度 \(O(T\sqrt[k+1]n)\),由於 \(k\) 隨機所以可過。

細節:我們需要用 powl(n,(long double)1/K) 進行開根,但由於指數是小數,所以還是有精度問題,我們還需要 roundl 進行四捨五入。

T2

TAG:逆序對、二維平面、掃描線。

這題我比賽時想到了:
如果選 \(i,j(i<j)\),那麼答案為 \(1+2\sum_{k=i+1}^j [a_i>a_k>a_j]\)

賽時寫了一個嚴格 \(O(n^2)\) 的做法,就是預處理 \(f_{i,j}\) 表示前 \(i\) 個數中 \(a_i\) 大於 \(j\) 的個數。

賽時還想到放到二維平面上,把每個位置看成一個點 \((i,a_i)\),那麼就是求一個矩形內點最多有多少個,要滿足矩形左上角和右下角是一個點。

賽時覺得左上角應該是一個上凸包,右下角是一個下凸包,然後時間不夠了,也沒有再深度思考。

而實際上左上角是字首最大值,右下角是字尾最小值。

發現我們列舉點對 \((i,j)\) 不好統計,而且點對也是 \(O(n^2)\) 的。

考慮 \((k,a_k)\) 對那些 \((i,j)\) 有貢獻。

發現滿足條件的 \(i\) 是一個區間 \(j\) 也是一個區間,記它們為 \([l_{1,k},r_{1,k}],[l_{2,k},r_{2,k}]\)

那麼再把它放到二維平面上,就是 \(x=[l_{1,k},r_{1,k}],y=[l_{2,k},r_{2,k}]\) 的一個矩形內的點有 \(+1\) 的貢獻。

那麼線段樹掃描線找到被最多矩形覆蓋的點即可。

把一個矩形 \([l_{1,k},r_{1,k}],[l_{2,k},r_{2,k}]\),分為 \((l_{1,k},r_{1,k},l_{2,k},1)\)\((l_{1,k},r_{1,k},r_{2,k}+1,-1)\) 即可。

T3

TAG:最小生成樹、Prim、Kruskal、線段樹、均攤、啟發式合併。

做法 1

考慮 Kruskal。

把點按點權重標號,然後用 vector 和並查集維護每一個連通塊內的點與每個點屬於哪一個連通塊,set 維護當前每個不同的連通塊編號。

對於給的邊就直接加;對於剩下的邊,我們每次嘗試向一個不同連通塊內的點連邊,失敗次數是 \(O(m)\) 的,成功次數是 \(O(n)\) 的,於是均攤複雜度就是正確的。

合併 vector 需要啟發式合併。

時間複雜度 \(O((n+m)\log n)\)

考場上想到了均攤,但是沒想到如何維護不同的連通塊,但是寫了一個相似的可以被卡掉的做法,但結果過了。

做法2

考慮 Prim,也就是每次找離連通塊內最近的點加入連通塊。

可以用線段樹維護離連通塊的距離與取出一個最近的點。

更新距離時,所有點的更新被給定的邊分成了 \(O(m+n)\) 個區間賦 \(a_i\)

時間複雜度 \(O((n+m)\log n)\)

T4

發現取帶權重心最優。

這題是動態尋找帶權重心,用倍增找。然後權值和需要用較複雜的容斥,需要預處理多個樹上 DP 陣列,同時還需要換根 DP。

用到的演算法並不難,一些結論也大概能猜到,就是在計算上的細節比較多。

總結

  • 題目難度不一定順序,不要花太多時間在看起來很簡單的題上。
  • 正難則反,多從不同角度思考問題。
  • 多從學過的演算法中找思路和靈感。
  • 使用 powl 時,記得用 ruondl,記得用 long double
  • 可以嘗試把序列上的統計問題放到二維平面上思考。
  • 統計 \(x\) 有多少個 \(y\) 時,可以考慮對於每個 \(y\) 思考它能貢獻到哪些 \(x\)
  • 可以用線段樹掃描線做最多覆蓋問題,用差分做矩形的掃描線。
  • 用啟發式合併合併 setvector 等。
  • 最短路、最近點可以考慮放到線段樹上最佳化更新和取點。
  • 樹上問題多考慮考慮重心或直徑,從中尋找性質。

day #-3(11.26)

P1445 [Violet] 櫻花

下午做了這一道奇葩的藍題,我想了一個小時都不會做。

題意大概是給定 \(n,1\le n\le 10^6\),問 \(1/x+1/y=1/(n!)\) 的正整數解的個數。

大概是這樣的:

\[xn!+yn!=xy \]

\[x(y-n!)=yn! \]

\[x=\dfrac{yn!}{y-n!} \]

於是可以得到 \(y>n!\),那麼設 \(y=n!+k\)\(k\) 是正整數。

帶入得

\[x=\dfrac{n!^2+kn!} k \]

\[x=\dfrac {n!^2} k+n! \]

於是對於任意 \(k\) 滿足 \(k\mid n!^2\),都有對應的一個 \(x\),也就有對應的一組 \((x,y)\)

於是答案就是 \(d(n!^2)\),考慮列舉每個質數 \(p\) 的倍數,計算 \(p\) 在每個數中的指數和,記為 \(cnt_p\),答案就是 \(\prod_p(cnt_p+1)\)

時間複雜度為 \(O(n\ln n)\)

day #-2(11.27)

今天掛了 90 pts!!!

原因是 T1 在本地過了大樣例後沒造大資料,也沒寫對拍,於是最終的程式碼陣列越界,然而測大樣例時陣列越界在 windows 下還能正常跑!
以後一定要造大資料或者在虛擬機器上跑,windows 不可信。


今天做信友隊。

T1

TAG:容斥、列舉。

T1 考慮如果兩個點集有交,那麼交的部分就減去,列舉一個點,再列舉兩個有交點集所在的位置,給它們打上一個減去的標記,這部分是 \(O(nmk^2)\)

然後列舉兩個點集計算答案,這部分是 \(O(n^2m^2)\)

於是時間和空間都是 \(10^8\) 級別。

T2

TAG:隨機化、調整法。

sb 出題人把 sb 腦電波題放在 T2,全場沒人過。

題目大意:對於一個 \(n*n\) 的方陣,每個格子是 \(0/1\),方案數定義為不經過 \(0\) 每次往下或往右,從左上角走到右下角的方案數。求構造一個方案數為 \(L\)\(n\le 30\) 的方陣。

這道題是隨機化,然而 sb 題解並沒有證明,程式碼也很好打,就是調整法,每次貪心地調整最大的,多隨機幾次初始矩陣就可以透過。

T3

TAG:揹包、增量構造、多項式、遞推。

這題是好題。

題目大意:給你 \(n\)\(m\) 列數字,每行選一個數,求在所有選擇方案中選出的數的中位數的期望。

賽時做法

考慮列舉最後的中位數 \(x\),然後計算中位數為 \(x\) 的方案數,考慮揹包設 \(f_{i,j,k}\) 表示前 \(i\) 行選了 \(j\) 個小於 \(x\)\(k\) 個等於 \(x\) 的方案數。

由於 \(k\) 上界的和為 \(O(nm)\) 於是複雜度為 \(O(n^3m)\)

我真的不懂為什麼 \(O(n^4)\)\(O(n^5)\) 都一樣只有 20pts,但願正式賽會好一點。


然後我就想怎麼最佳化,發現這樣求方案數的話,不同中位數之間是沒有關聯的。

考慮求中位數小於等於 \(x\) 的方案數 \(ans_x\),於是等於的就是 \(ans_x-ans_{x-1}\)

中位數小於等於 \(x\) 的方案數就要求至少有 \(\dfrac {n+1} 2\) 行選的數要小於等於 \(x\),發現這隻與每一行小於等於 \(x\) 的數的個數有關。

然後我就不會了,覺得這個東西還是需要揹包。

正解

我們考慮可以把揹包刻畫成多項式的形式,如果我們要求中位數小於等於 \(x\) 的,設 \(cnt_i\)為第 \(i\) 行小於等於 \(x\) 的個數,那麼就有 \(ans_x=[x^{\frac{n+1} 2}]F(x) =[x^{\frac{n+1}2}]\prod_{i=1}^n [(m-cnt_i)+cnt_ix]\)

考慮把所有 \(a\) 排序後依次加入 \(cnt\) 中,這樣就能算出每個 \(x\)\(ans_x\)

考慮加入第 \(i\) 行的 \(a\) 後多項式怎麼變化,考慮到有 \(O(nm)\) 個數,我們只需 \(O(n)\) 暴力修改多項式即可。

初始時 \(F(x)=m^n\)

然後 \(cnt_i\to cnt_i+1\),我們可以先除以再乘上。

先考慮乘上,如果我們乘上 \((a+bx)\),那麼設 \(f_i\)\(i\) 次項係數,則

\[f_0'=af_0 \]

\[f_i'=af_i+bf_{i-1} \]

移項後就能得到除法的式子,如果我們除以 \((a+bx)\),則

\[f_0=\dfrac {f_0'} a \]

\[f_i=\dfrac {f_i'-bf_{i-1}} a \]

發現乘法要從大往小做,除法要從小往大做。

T4

TAG:期望、DP、高斯消元。

這道題鑑定為沒有使用過高斯消元做期望問題,現在會了。

part 1

先判斷無解的,對 \(O(n^2)\) 個點建圖,從能出去的點跑搜尋看哪些點出不去。

part 2

對所有 \((i,v),|v|\le n\) 的點取出來,建邊然後跑高斯消元,時間為 \(O(n^6)\)

part 3

發現如果 \(v>O(\sqrt n)\) 那麼前面已經走了 \(O(n)\) 距離,已經走出去了,於是只需要 \(|v|\le O(\sqrt n)\) 的點。

時間複雜度為 \(O((n\sqrt n)^3)=O(n^{4.5})\)

part 4

再最佳化點數,考慮把一個點的期望表示成 \(f_i=C+\sum_j a_{i,j} f_j\)\(C\) 是常數。如果可以那麼就能做到 \(O(n^3)\)

考慮計算從 \((i,0)\) 開始,對於每一個 \(j\) 中間不經過其他 \((x,0)\) 到達 \((j,0)\)期望步數、機率,以及直接走出去的期望步數、機率
這裡的期望步數求的是已經乘過機率的,比方說設 \(q_i\) 表示起點走到它的機率,\(c_i\) 表示起點走到它的期望步數(沒有乘上 \(q_i\) 的)、我們求的實際上是 \(q_i,C_i(C_i=q_ic_i)\),因為 \(c_i\) 是不好轉移的。
而且我們最後算的也是乘上機率後的期望步數和,即 \(C_i\) 的和。

舉例:
\(1\to^{\frac 1 2}\to 2,1\to^{\frac 1 2}\to 3\)
就有:
\(c_2=c_3=1,q_2=q_3=\frac 1 2,C_2=C_3=\frac 1 2\)

而就有轉移,設 \(p_i\) 為走過出邊的機率:

\[q_i=\sum q_j p_j \]

\[C_i=q_i+\sum C_jp_j \]

發現對於 \(j<i\),一定始終有 \(v<0\),對於 \(j>i\) 一定始終有 \(v>0\),即轉移的圖是一個 DAG。

於是便可以 DP,這一部分是 \(O(n^3)\)

最後就表示成了,設 \(C'\) 表示直接走出去的期望步數,\(f_i=\sum_j q_{i,j} f_j+C'+\sum_j C_{i,j}\)

移項得 \(-f_i+\sum _j q_{i,j}f_j=-C'-\sum_jC_{i,j}\),高斯消元 \(O(n^3)\)

高斯消元的時候直接把無解的行列全變成 0 即可。

總結

  • 一定要寫對拍、造大資料、在虛擬機器上過編譯。
  • 揹包的轉移可以嘗試刻畫成多項式的形式。
  • 期望問題可以高斯消元,高斯消元可以嘗試最佳化點數,可以用 DP 先處理出特殊點之間的關係以嘗試最佳化點數。

day #-1

今天做線下模擬賽,同樣的,去實際考場斷網做比賽。

今天打的非常好,沒有掛分,\(100+100+80+30=310\),獲得了前 \(10\) 名。

賽時

今天是順序開題,先看 T1,想到在上一次的生成樹上改,似乎是經典結論:在樹上加一條邊後會形成一個環,把環上最大的邊取走就可以得到新的生成樹,我寫的是 \(O(n^2\log n+qn)\) 但實際上可以用 \(Prim\) 做到 \(O(n^2+nq)\),評測時也是 800ms 驚險地卡過了。
發現有的人 \(O(n^2\log n+qn\log n)\) 被卡了、還有人 \(4*5000^2\) MLE 了。

T2 很 sb 但還是驚險地過了。發現資料隨機,想到是神奇的做法。
一開始想到類似某次 ABC 的 G 題那樣,查詢列舉子集、修改再列舉子集,資料隨機可以做到 \(O(n+3^m)\),但沒有最佳化前景。
然後又打表發現後面的詢問的答案都非常小,於是考慮每次從小到大列舉修改的位數,然後發現它跑得飛快,大約 500ms。
其實這個做法和 bfs 搜尋是本質相同的,複雜度或許可以證明是正確的,大概就是因為本質是 \(m\) 維空間的最小曼哈頓距離,空間中的點一定會越來越密集,所以跟資料隨機並沒有什麼關係。

打完前題大概是 \(9:30\),比賽是 \(7:30\) 開始的,經過 2h 了,速度中規中矩。

T3 經過我艱辛的打表,終於發現了 \(A\) 性質的充要條件,我也沒有證明,但它對了。

獲得 80 pts 後其實還剩一個多小時,但我想先去看 T4。

T4 想到網路流,但是並沒有這一檔分,想著多騙一點,結果打完了發現建模錯了,或許根本不能網路流。
只剩半小時了,趕快去寫指數級別的暴力,寫完後還剩十多分鐘了。

後面就是放到虛擬機器上過編譯了一下。

其實我知道 T3 的正解和 \(A\) 性質僅差一步之遙,但就虧在 T4 把時間浪費在錯解上了,或許也是前面的題花的時間過多了。

而且 T3 的表的規律或許只是運氣好,其他人都是考慮放到二維平面上考慮然後得到相同的結論。

賽後

T3 就是在 A 性質上擴充套件一下,還是 DP 考慮最終序列。

考慮除了第一段的每一段的結尾,它不能是一個隱藏的數,否則這一位填 0 以後和它不能區分,而每一段的除了結尾的位置則可以是隱藏的數。

再考慮每一段最大值的限制,它和第一段有關,如果第一段的結尾是一個隱藏的數,那麼如果後面沒有出現過最大值,那麼無法與第一段的結尾為其它數的序列區分。也就是如果第一段的結尾為隱藏的數,那麼後面一定要出現最大值。

那麼就設 \(f_{i,j,0/1}\) 表示第 \(i\) 位填了 \(j\),有沒有出現過最大值。
\(j\) 等於 0 時,那麼上一個數為一段的結尾,就要求上一個數不能隱藏。

總結

  • 寫完程式碼要注意最佳化常數,看清資料範圍。
  • 最小生成樹可以使用 \(Prim\)\(Kruskal\)。稠密圖 \(Prim\) 優為 \(O(n^2)\),稀疏圖 \(Kruskal\) 優為 \(O(m\log m+\alpha(n))\)
    \(Prim\) 可以用優先佇列做到 \(O(m\log m)\)
  • 打表找規律,但不要找無意義的規律,不要在這方面浪費太多時間。
  • 嘗試畫圖,把數值放到二維平面上或許能很快得到結論。
  • 求經過若干操作得到最終序列有多少個,一般問什麼就從什麼方向考慮,即直接求最終序列滿足的條件,用 DP 等構造最終序列。
  • 寫網路流一定要建模正確,不然假了寫這麼長的網路流就太浪費時間了。

相關文章