1. 程式人生 > >luogu P5291 [十二省聯考2019]希望

luogu P5291 [十二省聯考2019]希望

bubuko lin 大於 clas \n 不包含 每次 二維 lock

luogu

loj

無論最終結果將人類歷史導向何處
\(\quad\)我們選擇
\(\quad\quad\)\(\large{希望}\)

誒我跟你講,這題超修鹹的

下面稱離連通塊內每個點距離不超過\(L\)的點為中心點.首先可以註意到,所有連通塊的共同的中心點一定是個連通塊,所以可以寫一個暴力狀壓,表示中心點狀態為\(S\)的方案數,然後隨便枚舉一個連通塊轉移即可

暴力代碼

中心點是連通塊很煩,考慮轉化一下.其實答案為只考慮中心點為一個點的方案減去只考慮中心點為一條邊上的兩個點的方案,因為考慮任意一個連通塊,在算中心點為一個點會算點數\(n\)次,在算中心點為一條邊會算邊數\(m\)次,然後又有連通塊一定滿足\(n-m=1\)

,所以這樣算每個連通塊都會被算一次

先考慮中心點為一個點,設\(f_{x,l}\)表示在\(x\)子樹內,包含\(x\),且到\(x\)最遠距離不超過\(l\)的連通塊個數,轉移就是\(f_{x,l}=\prod_{y\in son(x)} (f_{y,l-1}+1)\),就是每個點兒子可選或不選,然後還要設\(g_{x,l}\)表示不包含\(x\)子樹其他點,包含\(x\),且到\(x\)最遠距離不超過\(l\)的連通塊個數轉移是\(g_{x,l}=g_{fa,l-1}\prod_{y\in son(fa),y\neq x} (f_{y,l-2}+1)\).那麽這個點的貢獻就是\((f_{x,l}g_{x,l})^k\)

.一條邊也類似,只不過註意兩個到中心點的距離都要\(\le L\),所以是\((f_{x,l-1}(g_{x,l}-1))^k\)

這個暴力是\(O(nL)\)的,不過因為下標和深度有關,所以可以長鏈剖分優化.\(f\)的話,一個點先繼承重鏈狀態,然後輕兒子暴力乘上去.註意第二維大於輕兒子第二維最大值(也就是\(y\)往下最多能延伸多深,假設是\(dep_y\))的部分,是要乘上\(f_{y,dp_y}\)的,所以還要支持後綴乘,用個可持久化線段樹維護即可(因為要算g要還原每個\(x\)的dp值)

然後就是\(g\),首先重鏈,重兒子直接繼承當前節點狀態.對於輕兒子,看上去要轉移的狀態比較多,不過可以註意到一個東西,就是一個點\(x\)

有用的\(g\)的第二維的範圍是\([L-dp_x,L]\),那麽只要轉移這些範圍就好了,復雜度是\(\sum\)輕兒子的\(dep\),所以復雜度也是對的.註意轉移到兒子時要扣掉自己本身的貢獻,一個想法是用所有兒子\(f\)的乘積乘上自己\(f\)的逆元,但是自己的\(f\)是有可能為0的,所以轉化成前綴積乘後綴積的形式,後綴積可以在做的時候維護

然後那個可持久化線段樹有個log,很不優秀.然後我們又發現前綴積其實在做的時候已經求過了,所以我們只要開個棧棧序撤銷合並的操作就得到前綴積.然後那個後綴乘也可以看做是全部乘一個值然後前綴乘上逆元,所以考慮維護dp數組的乘法標記\(a\)和加法標記\(b\),我們在dp數組上存值\(x\),然後真實值應該是\(ax+b\),每次後綴乘\(c\),直接給\(a\)\(b\)乘上\(c\),然後前面的值暴力改成\((ax+b-bc)(ac)^{-1}\).還有就是可能\(c=0\),那麽在維護賦值標記\(pl,nm\),表示下標為\(pl\)之後的值都是\(nm\),每次把\(nm\)改成\(-b*a^{-1}\)即可

不過逆元的問題還是沒解決.我們發現要求逆元的值一定是\(f_{x,dep_x}\),那麽可以參考階乘求逆元,先把前綴積搞出來,然後求出最後一項的逆元,然後用前綴積遞推出就可以算出每個數的逆元

差不多了,然後剩下的就看你的了(逃

代碼

技術分享圖片

luogu P5291 [十二省聯考2019]希望