zoukankan      html  css  js  c++  java
  • 「APIO2016」烟花表演

    「APIO2016」烟花表演

    解题思路

    又是一道 solpe trick 题,观察出图像变化后不找一些性质还是挺难做的。

    首先令 (dp[u][i]) 为节点 (u) 极其子树所有叶子到 (u) 距离为 (i) 的最少代价,显然有

    [dp[u][i]=sum_{vin son(u)}min_{0leq j leq i}{dp[v][j]+|C(u,v)-(i-j)|} ]

    定义函数

    [f_u(x)=dp[u][x] , g_u(x)= min_{0leq ileq x}{dp[u][x]+|C(fa[u],u)-(x-i)|} ]

    可以得到 (f_u(x) = sum_{v in son(u)} g_v(x))

    不难证明,(f,g) 的图像都是一个下凸包,且相邻的段之间斜率变化为 (1) ,考虑由 (f_u(x))(g_u(x)) 的过程

    (L, R)(f_u) 最下面那条边的左右端点,(len=C(fa[u],u)) ,把过程看做对图像的操作,那么有:

    [g_u(x)= egin{cases} f_u(x)+len& x<L \ f_u(L)+len-(x-L) &Lleq x<L+len \ f_u(L) & L + len leq xleq R+len \ f_u(R)+(x-R)-len & x>R+len end{cases} ]

    考虑维护这个凸包的拐点,1,2,3操作合起来相当于删除凸包上 (L,R) 两个拐点,然后插入 (L+len,R+len) 这两个拐点。

    4操作只需要在之前把斜率 (geq1) 的拐点删除到只剩一个即可,不难证明对于非叶子节点,这样的拐点只有儿子数量 (-1) 个,删完之后要找的 (L,R) 就是当前最右边的两个拐点。

    那么对于每一个 (u) 只需要将所有儿子的 (g) 合并起来即可得到当前的 (f) ,可并堆/线段树合并实现都是 (mathcal O((n+m)log n))

    我们维护出来 (f_1) 的所有拐点之后,求答案只需要用 (f_1(0)) 的值减去所有斜率 (leq0) 的拐点的横坐标即可。


    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;
    }
    
  • 相关阅读:
    WCF 转 武胜
    NET(C#):GetHashCode不需要太复杂 转 武胜
    wcf KnownTypeAttribute 武胜
    memmove
    李开复谈创业失败十原因:经不住诱惑 直接山寨
    Linux使用Wine 安装source insight
    VM 中ubuntu下Eclipse ctrl+s 显示update conflict的问题
    ISO/OSI模型
    memset函数使用详解
    C语言中字符串的处理方式
  • 原文地址:https://www.cnblogs.com/mangoyang/p/10840745.html
Copyright © 2011-2022 走看看