1. 程式人生 > >[BZOJ4585][Apio2016]Fireworks 煙火表演(樹形 DP + 凸包 + 左偏樹)

[BZOJ4585][Apio2016]Fireworks 煙火表演(樹形 DP + 凸包 + 左偏樹)

Address

洛谷 P3642
BZOJ 4585
UOJ #205
LOJ #2568

Solution

  • NOIP 2018 之後 A 掉的第一道題,祭
  • 看上去是水題,但是發現邊權可以減一之後就不是那麼容易了
  • 很容易想到狀態: f [ u ] [
    i ] f[u][i]
    表示把 u u 到子樹內所有葉子的距離都調成 i
    i
    的最小代價(下面 l e n ( u , v
    ) len(u,v)
    為邊 ( u , v ) (u,v) 的長度)
  • f [ u ] [ i ] = v s o n ( u ) max j = 0 i { f [ j ] + l e n ( u , v ) ( i j ) } f[u][i]=\sum_{v\in son(u)}\max_{j=0}^i\{f[j]+|len(u,v)-(i-j)|\}
  • 可以大膽猜想 f [ u ] f[u] 是關於 i i 的下凸函式
  • 假設 u u 只有 v v 一個子節點
  • i [ l , r ] i\in[l,r] f [ v ] f[v] 取得最小值
  • 然後分類討論一下轉移:
  • (1) 0 i l 0\le i\le l
  • 這時候 j [ 0 , i ] j\in[0,i] ,最優的方案是把 ( u , v ) (u,v) 的邊權改成 0 0
  • f [ u ] [ i ] = f [ v ] [ i ] + l e n ( u , v ) f[u][i]=f[v][i]+len(u,v)
  • (2) l i l + l e n ( u , v ) l\le i\le l+len(u,v)
  • 這時候最優的方案是把 ( u , v ) (u,v) 的邊權改成 i l i-l
  • f [ u ] [ i ] = f [ v ] [ l ] + l e n ( u , v ) i + l f[u][i]=f[v][l]+len(u,v)-i+l
  • (3) l + l e n ( u , v ) i r + l e n ( u , v ) l+len(u,v)\le i\le r+len(u,v)
  • 這時候最優的方案是不修改 ( u , v ) (u,v) 的邊權
  • f [ u ] [ i ] = f [ v ] [ i l e n ( u , v ) ] f[u][i]=f[v][i-len(u,v)]
  • (4) i r + l e n ( u , v ) i\ge r+len(u,v)
  • 這時候最優的方案是把 ( u , v ) (u,v) 的邊權改成 i r i-r
  • f [ u ] [ i ] = f [ v ] [ r ] + i r l e n ( u , v ) f[u][i]=f[v][r]+i-r-len(u,v)
  • f [ u ] f[u] f [ v ] f[v] 看作分段函式,可以發現每個段都是一次的
  • 上面四個轉移相當於
  • (1)把橫座標 [ 0 , l ] [0,l] 內的直線上移 l e n ( u , v ) len(u,v) 個單位
  • (2)把橫座標 [ l , r ] [l,r] 內的直線右移 l e n ( u , v ) len(u,v) 個單位
  • (3)在橫座標 [ l , l + l e n ( u , v ) ] [l,l+len(u,v)] 內加入一條斜率為 1 -1 的直線
  • (4)在橫座標 [ r + l e n ( u , v ) , + ] [r+len(u,v),+\infty] 內加入一條斜率為 1 1 的直線
  • 我們考慮維護分段函式的拐點
  • 注意到每經過一個拐點,函式的斜率加一
  • 對於葉子節點,有兩個拐點 ( 0 , 0 ) (0,0) ( 0 , 0 ) (0,0)
  • 這是一棵樹,處理點 u u 時如何將 u u 轉移到 u u 父親 f a [ u ] fa[u]
  • 先說:最右端直線的斜率是 u 的子節點數(接下來會證明)
  • 用一個大根堆維護拐點橫座標
  • 先不斷彈出最右端斜率大於 1 1 的直線,使得最後一段直線的斜率為 1 1
  • 然後把斜率為 0 0 的直線段對應的兩個拐點右移 l e n ( u , f a [ u ] ) len(u,fa[u]) 個單位
  • <