P3642 [APIO2016]烟火表演 可并堆
作者:互联网
题意:
分析:
这题是 ZJOI2007时态同步 的加强版,那个题里面只能加边不能删边,而这个题允许删边
我们还是按照时态同步的想法来做,就是树上DP,我们令 \(f(i,j)\) 表示使 \(i\) 的子树内所有叶子节点到 \(i\) 的距离为 \(j\) 的最小代价,我们分析可以发现,
-
\(f(i,j)\) 是关于 \(i\) 的一个分段函数
分段区间的每一段区间一定是通过修改固定边数条路径,所以这一段分段函数的斜率就是修改路径的条数
-
\(f(i,j)\) 是一个下凸壳
感性理解:一定存在一个区间 \(l,r\) 使得 \(\forall x \ 满足f(i,l)\le f(i,x)\),那么 \(x\le l\) 时需要大量删边导致代价增大, \(x\ge r\) 时需要大量加边导致代价增大
由于树形DP需要将儿子的 贡献传递给父亲,那么我们现在考虑怎么将儿子的贡献累加到父亲上,
记关于儿子的分段函数为 \(f()\) ,关于父亲的分段函数为 \(g()\) , 儿子的斜率为 \(0\) 的区间(即代价最小的区间)为 \(L,R\) , 连接的边的权值为 \(w\) , 记 \(x\) 为转移到父亲的距离,根据定义 \(g(x)=min_{y=1}^{x}(f(y)+|w-(x-y)|)\)
分类讨论:
-
\(x\le L\) 时 由于 \(y\le x\) 所以 \(y< L\) ,即 \(f(y)\) 此时的斜率都小于等于 \(-1\) ,转移 $ 1$ 距离的代价都大于等于 \(1\) ,那么 \(y\) 取值越靠近 \(x\) 花费越少 ,所以当 \(y=x\) 时 \(g(x)=f(x)+w\)
-
\(L\le x\le L+w\) 当 \(y\ge L\) 时斜率为 \(0\) ,那么修改不影响代价,此时 \(y\) 越小 \(|w-(x-y)|\) 越小 总代价越小,所以我们尽可能从 \(L\) 转移过来 \(g(x)=f(L)+w-(x-L)\)
-
\(L+w\le x \le R+w\) 此时不用对 \(w\) 做任何增删就能直接从 \(L,R\) 转移过来,所以 \(g(x)=f(L)\)
-
\(R+w\le x\) 和 1 同理,由于此时 \(f(y)\) 的斜率都大于等于 \(1\) 所以转移 \(1\) 距离的代价都大于等于 \(1\),那么尽可能使得 \(y\) 斜率趋近 \(0\) 所以 \(g(x)=f(R)+(x-R)-w\)
我们得到了转移式如下
\[g(x) = \begin{cases} f(x)+w & x \le L \\ f(L)+w-(x-L) & L < x < L+w \\ f(L) & L+w<x\le R+w\\ f(R)+(x-R)-w & R+w<x \end{cases} \]我们观察几何意义可以发现
\([\ 0,L\ ]\) 向上平移 \(w\) 个单位长度 \([L,L+w\ ]\) 插入一条斜率为 \(-1\) 的线段 \([\ L,R\ ]\) 向右平移 \(w\) 个单位长度, \([R+w, \inf ]\) 插入一条斜率为 \(1\) 的线段
由于两个凸壳合并时会使得斜率增加 ,所以我们用一个小 \(trick\) 来维护凸壳,那就是记一下拐点的坐标,这样任意两个拐点之间的斜率就是 \(1\) (如果某个斜率不存在,我们只需要在同一个点多存几个斜率,直到这个斜率存在),那再回头看我们的转移式,由于每一次转移会使得新的凸壳右半部分斜率加 \(1\) 所以右侧的拐点数等于儿子个数 ,而我们要做的就是每一次弹出斜率大于 \(0\) 的点,直到找到斜率为 \(0\) 的区间 \([L,R]\) ,删除 \(R\) 点,插入 \(L+w,R+w\) 这样 \([L,L+w]\) 这段斜率顺便也变成了 \(-1\)
最后怎么计算答案那,我们只需要找到 \(1\) 节点对应的 \([L,R]\) 区间,由于 每一次合并都会使得 \([0,L]\) 区间向上移一条边权,所以 \(1\) 节点 \(f(0)=\sum w_i\) 然后直接删掉斜率小于 \(0\) 的拐点,输出 \([L,R]\) 区间的值
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 3e5+5;
int n,m,tot;
long long ans,val[maxn<<1];
int fa[maxn],deg[maxn],dis[maxn<<1],lc[maxn<<1],rc[maxn<<1],w[maxn],rt[maxn];
int new_node(long long x)
{
val[++tot]=x;dis[tot]=0;
return tot;
}
int merge(int x,int y)
{
if(!x||!y) return x|y;
if(val[x]<val[y]) swap(x,y);
rc[x]=merge(rc[x],y);
if(dis[lc[x]]<dis[rc[x]]) swap(lc[x],rc[x]);
dis[x]=dis[rc[x]]+1;
return x;
}
void pop(int &x)
{
x=merge(lc[x],rc[x]);
}
void work()
{
n=read();m=read();
for(int i=2;i<=n+m;i++)
{
fa[i]=read();w[i]=read();
ans+=w[i];deg[fa[i]]++;
}
for(int i=n+m;i>=2;i--)
{
if(i<=n)while(deg[i]-->1)pop(rt[i]);
long long l=val[rt[i]];pop(rt[i]);
long long r=val[rt[i]];pop(rt[i]);
rt[i]=merge(rt[i],merge(new_node(1ll*l+w[i]),new_node(1ll*r+w[i])));
rt[fa[i]]=merge(rt[fa[i]],rt[i]);
}
while(deg[1]--) pop(rt[1]);
while(rt[1]) ans-=val[rt[1]],pop(rt[1]);
printf("%lld\n",ans);
}
}
int main()
{
zzc::work();
return 0;
}
标签:rt,le,APIO2016,P3642,long,斜率,ch,烟火,pop 来源: https://www.cnblogs.com/youth518/p/14260557.html