點這裡看題目。
資料特殊,顯然需要資料分治。
此時 \(c=0\) 的點沒有貢獻,那麼就相當於 \(c\) 全部相等。這樣 \(c\) 最終的貢獻與 \(d\) 無關,我們把 \(c=1\) 的點全部模擬點亮一遍即可。
不難想到做樹形 dp 。我們可以想到這樣的狀態:
\(f(u,0/1,0/1)\):第乙個 \(0/1\) 表示 \(u\) 是否被激發,第二個表示 \(u\) 的父親是否被激發,在這個情況下將 \(u\) 子樹內的點全部激發的最小代價。
然後你發現這樣做複雜度不對無法處理兒子之間的轉移順序,或者說,我們無法有序確定哪些兒子在當前點之後被激發。其實可以,直接列舉全排列。
但注意到,如果乙個兒子 \(v\) 在 \(u\) 之前激發,那麼 \(v\) 就會給 \(u\) 以 \(c_v\) 的貢獻;如果 \(v\) 在之後,那麼 \(u\) 就會給 \(v\) 以 \(c_u\) 的貢獻。既然乙個兒子最多只有兩種狀態,我們就可以考慮0/1 揹包。
考慮狀態 \(f(u,0/1)\):表示 \(u\) 的父親是否激發,此情況下 \(u\) 子樹內全部激發的最小代價。
那麼中途可以用揹包進行轉移, \(g(i)\) 表示考慮完一些兒子之後,當前點接受的能量為 \(i\) 的最小代價。轉移顯然:
\[g(i)=\min\
\]最後 \(f(u,0/1)\) 都可以用 \(g\) 算出來。
注意到 \(c\) 很小,於是複雜度就是 \(o(cn^2)\) 。
小結:利用 0/1 揹包處理只含兩種情況的轉移順序的方法,具有借鑑意義。
#include #include const int maxn = 1e5 + 5, maxs = 1e4 + 5;
templatevoid read( _t &x )
while( '0' <= s && s <= '9' )
x *= f;
}templatevoid write( _t x )
template_t max( const _t a, const _t b )
template_t min( const _t a, const _t b )
struct edge
graph[maxn << 1];
int head[maxn], d[maxn], c[maxn];
int n, cnt;
void addedge( const int from, const int to )
namespace allone
void solve()
for( int i = 1 ; i <= n ; i ++ ) ans += max( d[i], 0 );
write( ans ), putchar( '\n' ); }}
namespace trivial
void solve() }
int main()
SHOI2015 超能粒子炮 改
設 f n k sum kc n i pmod 那麼根據盧卡斯定理我們知道 f n k sum kc times c c 0 times sum c i c 1 times sum c i c times sum c i c times sum c i sum c i times c 0 c 1 c...
SHOI2015 自動刷題機
我也想要乙個!顯然能夠看出來單調性 當n越大,能ac的題越少,反之越少。所以。很明顯的二分答案對吧。唯一一點不太一樣的是,它要求可能的最大值,和可能的最小值。那我們就寫兩個check,先求出來最大值,如果算出來的ac題數大於等於k,我們就選擇右區間。然後再0和最大值的區間中再次二分 減小帶的log的...
SHOI2015 超能粒子炮 改
求 sum 2333 n,k leq 10 如果直接套盧卡斯還是比較容易想到分塊求解的 由 c n i c times c 可知,i p 相同的組合數另一部分分別是 i p,i p 1,i p 2.這部分可以搓到一起 令 s n k sum 具體來說,將這部分相同的部分放到一起,剩下的地方直接計算 ...