Loj#6405-「ICPC World Finals 2018」征服世界【模拟费用流,左偏树】
正题
题目链接:https://loj.ac/p/6405
题目大意
给出\(n\)个点的一棵树,每个点有\(x_i\)个军队,需要\(y_i\)个军队,你可以移动军队,求使得满足所有点要求的情况下,军队移动路径和的最小值。
\(1\leq n\leq 250000\),军队总数和不超过\(10^6\)。
解题思路
一看就是费用流,但是数据范围很大所有是模拟费用流。
那么考虑贪心,因为我们要贪心所以我们很难强制满流,那么我们就定义一个流量会额外带上一个\(-\infty\)的权值,然后最后答案加上\(\infty\times c\)就好了。
那么考虑一个流量从\(x\)流到\(y\),费用是\(dep_x+dep_y-dep_{lca}\times 2-\infty\),如果一个是需要军队的,那么定义\(val_x=dep_x-\infty\),否则\(val_x=dep_x\)。
那么就可以视费用为\(val_x+val_y-dep_{lca}\times 2\),那么这些答案我们就可以在\(lca\)处维护了,设它们的\(lca\)为\(z\)。
考虑用可并堆储存子树内的\(val_x\)和\(val_y\),然后每次取出最小的直到\(val_x+val_y-dep_z\times 2\geq 0\)为止,但是我们还需要一个退流的操作,也就是可撤回的过程。这个过程我们的权值是相反的,定义新的\(val'_x=2\times dep_z-val_y\),同理\(val'_y=2\times dep_z-val_x\)就好了。
时间复杂度:\(O(n\log n)\)
code
#include
#include
#include
#define ll long long
using namespace std;
const ll N=3e6+10,inf=1e12;
struct Heap{
ll cnt,w[N],t[N][2],dis[N];
ll Merge(ll x,ll y){
if(!x||!y)return x|y;
if(w[x]>w[y])swap(x,y);
t[x][1]=Merge(t[x][1],y);
if(dis[t[x][0]]0){
for(int i=0;i