[APIO2016]烟火表演

链接:https://www.luogu.org/problemnew/show/P3642

跟上一道题类似但更难,首先也是观察出在某个节点代价是下凸的函数,并且得到转移方程:

1.x<=L f'(x)=f(x)+w; 

2.L<=x<=L+w f'(x)=f(L)+w-(x-L)

3.L+w<=x<=R+w f'(x)=f(L) 

4.R+w<=x f'(x)=f(L)+(x-R)-w

意思是当前节点考虑到父亲的那条边权为w的边对于不同x取值的转移,【L,R】表示最小代价的左右端点,而因为在叶子节点初始化只有一个点(实际上是两个中间是长度为0的斜率为0的线)左边斜率-1右边+1,再看转移操作可以画图发现实际上是将函数整体右移了w距离。发现这个性质就又能像之前那题通过维护关键点(转折点)得出答案了,对每个点开可并大根堆,维护的点到R(当前最优解能取到最右边的),在某节点合并儿子子树时,发现会多(孩子数-1)这么多个斜率大于0的,pop掉即可的当前L,R,然后发现整体右移操作其实就相当于删除L,R然后插入L+w和R+w,最后因为发现f(0)为所有边边权和,反推回L处答案即可。

代码:

#include
#define ll long long
using namespace std;
const int N=6e5+100;

struct Heap{
	int ls,rs;ll w;
	Heap(){};
	Heap(int ls,int rs,ll w):ls(ls),rs(rs),w(w){};
}nd[N*2];
ll sum=0;
int n,m,fa[N],w[N],in[N],rt[N],hnum=0;
vectorson[N];

int mg(int x,int y)
{
	if(!x||!y)return x+y;
	if(nd[x].w

 

你可能感兴趣的:(刷题集,训练集,笔记)