「APIO2016」煙花表演

「APIO2016」煙花表演

解題思路c++

又是一道 solpe trick 題,觀察出圖像變化後不找一些性質仍是挺難作的。git

首先令 \(dp[u][i]\) 爲節點 \(u\) 極其子樹全部葉子到 \(u\) 距離爲 \(i\) 的最少代價,顯然有
\[ dp[u][i]=\sum_{v\in son(u)}\min_{0\leq j \leq i}\{dp[v][j]+|C(u,v)-(i-j)|\} \]
定義函數
\[ f_u(x)=dp[u][x] , g_u(x)= \min_{0\leq i\leq x}\{dp[u][x]+|C(fa[u],u)-(x-i)|\} \]
能夠獲得 \(f_u(x) = \sum_{v \in son(u)} g_v(x)\)api

不難證實,\(f,g\) 的圖像都是一個下凸包,且相鄰的段之間斜率變化爲 \(1\) ,考慮由 \(f_u(x)\)\(g_u(x)\) 的過程函數

\(L, R\)\(f_u\) 最下面那條邊的左右端點,\(len=C(fa[u],u)\) ,把過程看作對圖像的操做,那麼有:
\[ g_u(x)= \begin{cases} f_u(x)+len& x<L \\ f_u(L)+len-(x-L) &L\leq x<L+len \\ f_u(L) & L + len \leq x\leq R+len \\ f_u(R)+(x-R)-len & x>R+len \end{cases} \]
考慮維護這個凸包的拐點,1,2,3操做合起來至關於刪除凸包上 \(L,R\) 兩個拐點,而後插入 \(L+len,R+len\) 這兩個拐點。spa

4操做只須要在以前把斜率 \(\geq1\) 的拐點刪除到只剩一個便可,不難證實對於非葉子節點,這樣的拐點只有兒子數量 \(-1\) 個,刪完以後要找的 \(L,R\) 就是當前最右邊的兩個拐點。code

那麼對於每個 \(u\) 只須要將全部兒子的 \(g\) 合併起來便可獲得當前的 \(f\) ,可並堆/線段樹合併實現都是 \(\mathcal O((n+m)\log n)\)get

咱們維護出來 \(f_1\) 的全部拐點以後,求答案只須要用 \(f_1(0)\) 的值減去全部斜率 \(\leq0\) 的拐點的橫座標便可。it


code

/*program by mangoyang*/
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
}
const int N = 1000005;
ll ans;
int fa[N], len[N], deg[N], n, m;
__gnu_pbds::priority_queue<ll> pq[N];
int main(){
    read(n), read(m);
    for(int i = 2; i <= n + m; i++){
        read(fa[i]), deg[fa[i]]++;
        read(len[i]), ans += len[i];
    }
    for(int i = n + m; i > 1; i--){
        ll x = 0, y = 0;
        if(i <= n){
            for(int j = 1; j < deg[i]; j++) pq[i].pop();
            x = pq[i].top(), pq[i].pop();
            y = pq[i].top(), pq[i].pop();
        }
        pq[fa[i]].push((ll) x + len[i]);
        pq[fa[i]].push((ll) y + len[i]);
        pq[fa[i]].join(pq[i]);
    }   
    for(int i = 1; i <= deg[1]; i++) pq[1].pop();
    while(!pq[1].empty())
        ans -= pq[1].top(), pq[1].pop();
    cout << ans << endl;
    return 0;
}
相關文章
相關標籤/搜索