zoukankan      html  css  js  c++  java
  • 【UOJ455】【UER #8】雪灾与外卖(模拟费用流)

    点此看题面

    大致题意: 给定一条数轴,有(n)个位置各有一只兔子,还有(m)个位置各有(c_i)个洞,每个地方的洞有一个相同的权值(v_i)。每只兔子必须走到一个洞中,每个洞最多进一只兔子。求最小化所有兔子走的总距离以及所进洞的权值总和。

    模拟费用流

    考虑所有(c_i=1)的情况,就是一个模拟费用流的常规模型。

    对于各种可能出现的情形分别考虑:(注意,以下的代价均指在堆中的值

    • 考虑一只兔子(i)如果选择了一个代价为(V)的洞,对答案贡献就是(x_i+V)
      • 如果之后一个洞(j)替换了这个洞,答案应该加上(y_j+v_j-2x_i-V)(注意,兔子坐标对答案的贡献从正变成了负,故要减去(2x_i))。也就是说,要加入一只代价为(-2x_i-V)的兔子
      • 这种情况下别的兔子不能替换这只兔子,否则这只兔子就没有洞了,违背了每只兔子一定要进洞的原则。
    • 考虑一个洞(j)如果选择了一只代价为(W)的兔子,对答案贡献就是(y_j+v_j+W)
      • 如果之后一只兔子(i)替换了这只兔子,答案应该加上(x_i-2y_j-W)。也就是说,要加入一个代价为(-2y_j-W)的洞。(注意,这里不会因为原兔子被替换而违背每只兔子一定要进洞的原则,因为它在洞(j)选择它之前就有一个匹配的洞,洞(j)被抢走后它又会找回原先的洞,一切都回到洞(j)选择它之前的样子
      • 如果之后一个洞(k)替换了这个洞,答案应该加上(y_k+v_k-y_j-v_j)。也就是说,要加入一只代价为(-y_j-v_j)的兔子

    看起来很绕,不过其实只要知道,兔子已经不是单纯的兔子,洞已经不是单纯的洞,但兔子和洞之间依旧存在着匹配关系,就仍旧可以用来解决这个问题。

    无论怎样,之所以问题会这么复杂,只是为了高效地完成退流操作而已。

    分身

    分身的情况其实非常简单啦。

    我们直接把一个位置上的洞用pair绑起来,然后同样按照上面的做法去做即可。

    复杂度是可以证明的,反正我不会就是了。

    代码

    #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 100000
    #define LL long long
    #define INF 1e12
    using namespace std;
    int n,m;struct Data
    {
    	int x,v,c;I bool operator < (Con Data& o) Con {return x<o.x;}//存储信息
    }s[2*N+5];
    struct Pair
    {
    	LL v;int c;I Pair(Con LL& a=0,CI b=1):v(a),c(b){}//把同样的洞绑成pair
    	I bool operator < (Con Pair& o) Con {return v>o.v;}
    };priority_queue<Pair> A,B;//A存储洞,B存储兔子
    int main()
    {
    	RI i,k;LL g=0;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&s[i].x),s[i].v=-1;
    	for(i=1;i<=m;++i) scanf("%d%d%d",&s[n+i].x,&s[n+i].v,&s[n+i].c),g+=s[n+i].c;if(g<n) return puts("-1"),0;//判无解
    	LL ans=0;Pair t;for(sort(s+1,s+n+m+1),A.push(Pair(INF,n)),i=1;i<=n+m;++i) if(!~s[i].v)//初始加入n个INF保证每只兔子一定要进洞
    	{
    		t=A.top(),A.pop(),ans+=s[i].x+t.v,--t.c&&(A.push(t),0),B.push(Pair(-2LL*s[i].x-t.v,1));//兔子只有1只,处理较为简单
    	}
    	else
    	{
    		g=0;W(!B.empty()&&s[i].c)//只要还能配对就不断搞
    		{
    			if(t=B.top(),s[i].x+s[i].v+t.v>=0) break;B.pop();//洞无需用完,当会使答案变劣就结束
    			k=min(s[i].c,t.c),s[i].c-=k,t.c-=k,g+=k,ans+=(s[i].x+s[i].v+t.v)*k,//配对k次
    			t.c&&(B.push(t),0),A.push(Pair(-2LL*s[i].x-t.v,k));//入堆
    		}
    		g&&(B.push(Pair(-s[i].x-s[i].v,g)),0),s[i].c&&(A.push(Pair(-s[i].x+s[i].v,s[i].c)),0);//把同样的兔子/洞一起加入堆中
    	}
    	return printf("%lld
    ",ans),0;//输出答案
    }
    
  • 相关阅读:
    TyporaRecord
    c# 串口 应答式顺序下发命令 循环 间隔发送指令
    WPF 如何在单独的配置文件中使用Log4net
    UWP VisualStateManager
    USB通信
    UWP RelativePanel
    JSON 序列化与反序列化
    Unity 依赖注入的三种常用模板
    IOC Unity容器的基本使用
    使用EF完成基于SQLite的CodeFirst
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/UOJ455.html
Copyright © 2011-2022 走看看