题目

题目大意

给你一个有向图,每个点开始有一定的水量(可能为负数),可以通过边流到其它点。

每条边的流量是有上下界的。

每个点的水量可以增加或减少(从外界补充或泄出到外界),但是需要费用,和增加(减少)流量呈正比例函数关系。

每条边的流量也需要费用,费用和流量呈二次函数关系(常数项为\(0\))。

问将所有水流完的最小花费。


思考历程

这显然是一道上下界最小费用可行流嘛!

有源有汇的上下界可行流的做法:建立超级源\(ss\)和超级汇\(tt\)(和\(S\)、\(T\)),对于\(u\)到\(v\)的容量为\([low,up]\)的边。这时从\(ss\)到\(v\)连一条容量为\(low\)的边,\(u\)到\(tt\)连一条容量为\(low\)的边,\(u\)到\(v\)连一条\(up-low\)的边。并且要从\(T\)到\(S\)连一条容量为无限大的边。

具体原因不再赘述。

对于每个点的初始水量,如果为正数,就从\(S\)向它连一条容量上下界都为水量的边,费用为\(0\)。

如果为负数,就从\(T\)向它连一条容量上下界都为水量的绝对值的边,费用为\(0\)。

对于每个点和外界之间的关系,可以从\(S\)到它连一条上限为无限大的边,它到\(T\)连一条上限为无限大的边,费用由题目给定。

可是最麻烦的来了,这个二次函数该怎么处理呢?

想不出来……

最终交了个错误的程序上去,成功爆0……


正解

前面的都差不多了,就只有二次函数的那一部分。

二次函数为\(y=ax^2+bx\),直接搞似乎不行,那就试着将它们拆开,变成\(a+b\)、\(3a+b\)、\(5a+b\)……这些边。每条边的容量为\(1\)。

具体来说,当\(x\)变成\(x+1\)时,费用就会新增\(a(2x+1)+b\)

由于我们跑的是最小费用可行流,一定会先跑更小的边。所以这种方法是不可能WA的。

但是如果直接这样拆,边会很多啊!想一想,要拆成最多\(100\)条边……

于是就有了动态加边大法!

一开始只加入\(a+b\)的边,如果这条边被流满,那就加一条\(3a+b\)的边,以此类推……

当然,由于上下界的问题,一开始加的并不是\(a+b\)。先将最低费用\(a*low^2+b*low\)加入答案。为了使费用不重复计算,\(ss\)到\(v\)和\(u\)到\(tt\)的边就不需要费用了,只是中间那条\(u\)到\(v\)的边初始费用变成了\(a(2low+1)+b\)。

至此整道题就解决了。


代码

独爱zkw费用流……

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define min(a,b) ((a)<(b)?(a):(b))
#define INF 1000000000
#define N 1000
int n,m;
struct EDGE{
int to,c,w;
EDGE *las;
int sp;
} e[100000];
int ne;
EDGE *last[N];
#define rev(ei) (e+(int((ei)-e)^1))
inline void link(int u,int v,int c,int w,int sp=0){
e[ne]={v,c,w,last[u],sp};
last[u]=e+ne++;
e[ne]={u,0,-w,last[v],0};
last[v]=e+ne++;
}
int nsp;
int mx[100000],ad[100000];
int ex[100000];
inline void link2(int u,int v,int low,int up,int a,int b){
nsp++;
mx[nsp]=up,ad[nsp]=a*2,ex[nsp]=low+1;
e[ne]={v,1,a*(2*low+1)+b,last[u],nsp};
last[u]=e+ne++;
e[ne]={u,0,-(a*(2*low+1)+b),last[v],0};
last[v]=e+ne++;
}
int s,t,ss,tt;
int mincost;
int dis[N];
int vis[N],BZ;
int dfs(int x,int s){
if (x==tt){
mincost+=dis[ss]*s;
return s;
}
int have=s;
vis[x]=BZ;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (vis[ei->to]!=BZ && ei->c && dis[x]==dis[ei->to]+ei->w){
int t=dfs(ei->to,min(have,ei->c));
ei->c-=t,rev(ei)->c+=t,have-=t;
if (ei->sp && ex[ei->sp]<mx[ei->sp]){
link(x,ei->to,1,ei->w+ad[ei->sp],ei->sp);
ex[ei->sp]++;
ei->sp=0;
}
if (!have)
return s;
}
return s-have;
}
inline bool change(){
int d=INF;
for (int i=1;i<=n+4;++i)
if (vis[i]==BZ)
for (EDGE *ei=last[i];ei;ei=ei->las)
if (vis[ei->to]!=BZ && ei->c)
d=min(d,dis[ei->to]+ei->w-dis[i]);
if (d==INF)
return 0;
for (int i=1;i<=n+4;++i)
if (vis[i]==BZ)
dis[i]+=d;
return 1;
}
inline void zkw(){
do
do
BZ++;
while (dfs(ss,INF));
while (change());
}
int main(){
scanf("%d%d",&n,&m);
s=n+1,t=n+2,ss=n+3,tt=n+4;
for (int i=1;i<=n;++i){
int left,in,out;
scanf("%d%d%d",&left,&in,&out);
if (left>0)
link(ss,i,left,0),link(s,tt,left,0);
else if (left<0)
link(ss,t,-left,0),link(i,tt,-left,0);
link(s,i,INF,in);
link(i,t,INF,out);
}
link(t,s,INF,0);
for (int i=1;i<=m;++i){
int u,v,a,b,low,up;
scanf("%d%d%d%d%d%d",&u,&v,&a,&b,&low,&up);
mincost+=a*low*low+b*low;
if (a){
if (low)
link(ss,v,low,0),link(u,tt,low,0);
if (up-low)
link2(u,v,low,up,a,b);
}
else{
if (low)
link(ss,v,low,b),link(u,tt,low,0);
if (up-low)
link(u,v,up-low,b);
}
}
zkw();
printf("%d\n",mincost);
return 0;
}

总结

题目的难点主要在拆边,事实上,这似乎有点套路啊……

所以当直接做不方便时,可以试着作差,然后就出来一些东西……

[JZOJ3302] 【集训队互测2013】供电网络的更多相关文章

  1. [JZOJ3303] 【集训队互测2013】城市规划

    题目 题目大意 求\(N\)个点的简单无向图的方案数(有编号). 结果对\(1004535809\)取模. 思考历程 感觉这个问题非常经典. 当时想到了一堆式子,但都觉得可能会有重和漏,于是弃掉了-- ...

  2. 【loj2461】【2018集训队互测Day 1】完美的队列

    #2461. 「2018 集训队互测 Day 1」完美的队列 传送门: https://loj.ac/problem/2461 题解: 直接做可能一次操作加入队列同时会弹出很多数字,无法维护:一个操作 ...

  3. 【2018集训队互测】【XSY3372】取石子

    题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...

  4. 洛谷 P4463 - [集训队互测 2012] calc(多项式)

    题面传送门 & 加强版题面传送门 竟然能独立做出 jxd 互测的题(及其加强版),震撼震撼(((故写题解以祭之 首先由于 \(a_1,a_2,\cdots,a_n\) 互不相同,故可以考虑求出 ...

  5. UOJ#191. 【集训队互测2016】Unknown 点分治 分治 整体二分 凸包 计算几何

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ191.html 题目传送门 - UOJ191 题意 自行移步集训队论文2016中罗哲正的论文. 题解 自行 ...

  6. LOJ3069. 「2019 集训队互测 Day 1」整点计数(min_25筛)

    题目链接 https://loj.ac/problem/3069 题解 复数真神奇. 一句话题意:令 \(f(x)\) 表示以原点 \((0, 0)\) 为圆心,半径为 \(x\) 的圆上的整点数量, ...

  7. UOJ#191. 【集训队互测2016】Unknown

    题意:维护一个数列,每个元素是个二维向量,每次可以在后面加一个元素或者删除一个元素.给定P(x,y),询问对于[l,r]区间内的元素$S_i$,$S_i \times P$的最大值是多少. 首先简单地 ...

  8. 【集训队互测2015】Robot

    题目描述 http://uoj.ac/problem/88 题解 维护两颗线段树,维护最大值和最小值,因为每次只有单点查询,所以可以直接在区间插入线段就可以了. 注意卡常,不要写STL,用链表把同类修 ...

  9. EZ 2018 05 06 NOIP2018 慈溪中学集训队互测(五)

    享受爆零的快感 老叶本来是让初三的打的,然后我SB的去凑热闹了 TM的T2写炸了(去你妹的优化),T1连-1的分都忘记判了,T3理所当然的不会 光荣革命啊! T1 思维图论题,CHJ dalao给出了 ...

随机推荐

  1. (转)Java中Image的水平翻转、缩放与自由旋转操作

    来自:http://cping1982.blog.51cto.com/601635/130066/ 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责 ...

  2. 将Java和Javac的命令在控制台的输出重定向到txt文件

    当我们在Windows控制台窗口执行程序时,输入如下命令: demo.exe > out.txt 就可以把demo程序的输出重定向到out.txt文件里面. 但是这种方法对于java和javac ...

  3. Tools: geos 使用指南

    1. 下载geos 2. 进入VS开发人员命令提示3.依次执行如下命令 >VCVARS32.BAT>cd D:\DevTool\geos-3.7.0>atuogen.bat>n ...

  4. 关于sublime使用中写less代码高亮显示问题

    一开始在没有配置的情况下在sublime中写less代码是不会有高亮显示的.下面说一下配置过程 一.安装Less2Css模块 打开sublime,ctrl+shift+p,输入package cont ...

  5. 通过js渲染高层级DOM实现网页加水印

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. Ruby 命令行选项

    Ruby 命令行选项 Ruby 一般是从命令行运行,方式如下: $ ruby [ options ] [.] [ programfile ] [ arguments ... ] 解释器可以通过下列选项 ...

  7. bzoj1057: [ZJOI2007]棋盘制作 [dp][单调栈]

    Description 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源 于易经的思想,棋盘是一个8*8大小的黑白相间的方阵,对应八八六十四卦,黑白对应 ...

  8. NX二次开发-UFUN拉伸函数UF_MODL_create_extruded2

    NX9+VS2012 //NX二次开发中常用拉伸函数为UF_MODL_create_extruded2,但是此函数不能拉伸片体, //想要拉伸片体用函数UF_MODL_create_extruded. ...

  9. NX二次开发-UFUN工程图表格注释获取某一列的tag函数UF_TABNOT_ask_nth_column

    NX9+VS2012 #include <uf.h> #include <uf_tabnot.h> #include <NXOpen/Part.hxx> #incl ...

  10. Spring声明式事务的实现方式选择(JDK动态代理与cglib)

    1.简介 Spring声明式事务的具体实现方式是动态决定的,与具体配置.以及事务代理对象是否实现接口等有关. 2.使用JDK动态代理的情况 在满足下面两个条件时,Spring会选择JDK动态代理作为声 ...