zoukankan      html  css  js  c++  java
  • 【洛谷6943】[ICPC2018 WF] Conquer The World(模拟费用流)

    点此看题面

    • 给定一棵(n)个点的树,每条边有一个长度。
    • (i)个点上原本有(x_i)个人,需要有至少(y_i)个人,求所有人移动总路长的最小值。
    • (nle2.5 imes10^5,sum y_ilesum x_ile10^6)

    模拟费用流

    显然一个点上原有的(min{x_i,y_i})个人是不会动的。

    因此(x_i>y_i)的情况可以视作有(x_i-y_i)只兔子,(x_i<y_i)的情况可以视作有(y_i-x_i)个洞。

    由于这道题所有洞都必须匹配,需要给每个洞加上一个(-INF)的权值,使得选择所有洞一定最优。

    我们在每个点合并它不同子树间的兔子和洞。

    假设有兔子(x)和洞(y),当前点((LCA))为(z),它们之间的路径长度就是(d_x+d_y-2d_z)

    由于当前点确定时(-2d_z)为定值,因此把兔子的权值(A_x)视为(d_x),洞的权值(B_y)视为(d_y-INF),然后分别开一个小根堆,每次取出各自的堆顶尝试更新答案即可。(更新答案的条件:(A_x+B_y-2d_z<0)

    但我们还要考虑退流,如果我们想让兔子(x)能换成和另一个洞匹配,相当于新建一只权值为(2d_z-B_y)的兔子(x'),这两只兔子权值相加刚好消得只剩(A_x),除去了原本的洞的贡献。同理,想让洞(y)能换成和另一只兔子匹配,相当于新建一个权值为(2d_z-A_x)的洞(y')

    因为这里的堆需要合并,写个左偏树即可。

    代码:(O(VlogV))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 250000
    #define LL long long
    #define INF (LL)1e12
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
    using namespace std;
    int n,a[N+5],ee,lnk[N+5];struct edge {int to,nxt,v;}e[2*N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }using namespace FastIO;
    class LeftistTree
    {
    	private:
    		#define SZ 1000000
    		int Nt,Rt[N+5];struct node {LL V;int D,S[2];}O[10*SZ+5];
    		I int Merge(RI x,RI y)//合并
    		{
    			if(!x||!y) return x|y;O[x].V>O[y].V&&(swap(x,y),0);
    			O[O[x].S[1]=Merge(O[x].S[1],y)].D>O[O[x].S[0]].D&&(swap(O[x].S[0],O[x].S[1]),0);
    			return O[x].D=O[O[x].S[1]].D+1,x;
    		}
    	public:
    		I LeftistTree() {O[0].D=-1;}I bool Empty(CI x) {return !Rt[x];}//初始化;判断是否为空
    		I void Ins(CI x,Con LL& v) {O[++Nt].V=v,Rt[x]=Merge(Rt[x],Nt);}//增加一个新元素
    		I void Union(CI x,CI y) {Rt[x]=Merge(Rt[x],Rt[y]);}//合并
    		I void Pop(CI x) {Rt[x]=Merge(O[Rt[x]].S[0],O[Rt[x]].S[1]);}//弹出堆顶
    		I LL Q(CI x) {return O[Rt[x]].V;}//询问堆顶值
    }A,B;
    LL ans;I void Calc(CI x,CI y,Con LL& d)//让x的兔子和y的洞配对
    {
    	LL a,b;W(!A.Empty(x)&&!B.Empty(y)&&(a=A.Q(x))+(b=B.Q(y))-2*d<0)//A[x]+B[x]-2*d<0
    		ans+=a+b-2*d,A.Pop(x),B.Pop(y),A.Ins(x,2*d-b),B.Ins(y,2*d-a);//更新答案,新建兔子和洞以实现退流
    }
    I void dfs(CI x,CI lst,Con LL& d)
    {
    	W(a[x]>0) A.Ins(x,d),--a[x];W(a[x]<0) B.Ins(x,d-INF),ans+=INF,++a[x];//把兔子/洞加入堆中
    	for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(dfs(e[i].to,x,d+e[i].v),
    		Calc(x,e[i].to,d),Calc(e[i].to,x,d),A.Union(x,e[i].to),B.Union(x,e[i].to),0);//合并不同子树间的兔子和洞
    }
    int main()
    {
    	RI i,x,y,z;for(read(n),i=1;i^n;++i) read(x,y,z),add(x,y,z),add(y,x,z);
    	for(i=1;i<=n;++i) read(x),read(y),a[i]=x-y;return dfs(1,0,0),printf("%lld
    ",ans),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    TCP/IP
    Socket通信
    Dubbo详解
    高并发详解
    P3-DataBase
    JAVA基础学习之路(十)this关键字
    [SHELL]输出目录下所有的可执行文件,批量创建用户
    JAVA基础学习之路(八)[1]String类的基本特点
    [MYSQL][2]索引
    [MYSQL][1]创建,修改,删除表
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6943.html
Copyright © 2011-2022 走看看