[ZJOI2010]贪吃的老鼠 网络流
题解:
这是一道强题emmmm,做法非常巧妙,,,我也是看了好久大佬题解才看明白一点
首先考虑没有限制的情况,即n个老鼠可以在同一时刻吃同一块奶酪
对各个时间段拆点,连奶酪 ---> 老鼠(反过来也是一样的,只不过不方便),相连的奶酪要符合时间段的限制,
相当于把老鼠拆成很多个小时刻,连向这个时刻它可以吃的奶酪,流量为它在这段时间内可以吃的奶酪总量,
限流可以在汇点到老鼠的路径上进行。
但这个并不能满足同一时刻一块奶酪只能被一个老鼠吃这个条件,因此我们对老鼠再拆点,
把每个老鼠拆成的小时刻再根据速度差分,
比如8 4 2 1,四只老鼠。差分后就是:
8 - 4 = 4;
4 - 2 = 2;
2 - 1 = 1;
1 - 1 = 1;
然后按照编号来定权值
流量就为编号 * 速度(差分后) * 时间;
为什么这样?
8 = 4 + 2 + 1 + 1
4 = 2 + 1 + 1
2 = 1 + 1
1 = 1
可以很明显看到这是一个三角形,且每层都是相同的数字,对应到我们差分数组,对于每个差分后的速度,刚好有编号个,
这样就可以保证总的流量合法了。
那为什么这样可以保证同一时刻只有一只老鼠呢?
可以这样感性的理解:
注意到任意一只老鼠都可以由差分数组凑出,那么不管网络流怎样跑出答案,我们都可以通过分解一下流量,加加减减之类的数学方法凑成这几只老鼠,因此是合法的。
也就是说网络流跑出的东西也许跟原图不一样,但它可以用来判断是否合法(满流即合法),这就够了。
因此我们二分答案,每次都重新建图,跑最大流,满流为true,else 为 false。
代码有点长(打的isap),改成dinic应该会短很多
(数组开这么小是卡常后的结果,,,,)
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 5000
#define ac 20000
#define eps 1e-6
int x, n, m, T, cnt, tmp, ss, tt;
int p[AC], v[AC], last[AC];
double all, ll, rr, mid, ans, addflow;
double r[AC], d[AC], haveflow[ac], t[AC];
int Head[AC], Next[ac], date[ac], tot;
int good[AC], have[AC], c[AC];
int q[AC], tail, head;
/*神奇的网络流,,,,
对每个时间点进行离散化*/
inline int read()
{
int x = ;char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline void upmin(double &a, double b)
{
if(b < a) a = b;
} inline void add(int f, int w, double S)
{
date[++tot] = w, Next[tot] = Head[f], haveflow[tot] = S, Head[f] = tot;
date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
//printf("%d ---> %d %.4lf\n",f,w,S);
} bool bfs()
{
int x, now;
head = tail = ;
c[tt] = , have[] = , q[++tail] = tt;
while(head < tail)
{
x = q[++head];
for(R i = Head[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i ^ ] && !c[now])
{
c[now] = c[x] + ;
q[++tail] = now;
++have[c[now]];
}
}
}
memcpy(good, Head, sizeof(Head));
return c[ss];
} void aru()
{
while(x != ss)
{
haveflow[last[x]] -= addflow;
haveflow[last[x] ^ ] += addflow;
x = date[last[x] ^ ];
}
ans += addflow;
} double isap()
{
int now; bool done;
x = ss, addflow = INT_MAX;
while(c[ss] != ac + )
{
if(x == tt) aru(), addflow = INT_MAX;
done=false;
for(R i = good[x]; i ; i = Next[i])
{
now = date[i];
if(c[now] == c[x] - && haveflow[i])
{
last[now] = i;
upmin(addflow, haveflow[i]);
good[x] = i;
done = true;
x = now;
}
}
if(!done)
{
int go = ac;
for(R i = Head[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i] && c[now]) go = c[now];
}
if(!(--have[c[x]])) break;
++have[c[x] = go + ];
good[x] = Head[x];
if(x != ss) x = date[last[x] ^ ];//这是回到上一个节点啊
}
}
return ans;
} inline bool cmp(double a, double b)
{
return a > b;
} void pre()
{
tot = , all = ;
n = read() ,m = read();
for(R i = ; i <= n; i++)
{
p[i] = read(), r[i] = read(), d[i] = read();
all += (double)p[i];
}
for(R i = ; i <= m; i++) v[i] = read();
sort(v + , v + m + , cmp);//error!!!老鼠是m只!!!!不是n只!!!
rr = (double) all / (double)v[] + 1.0, ll = ;
for(R i = ; i < m; i++) v[i] -= v[i + ];//对速度差分
} void build()
{
tot = , ans = ;
memset(Head, , sizeof(Head));//应该要放这里重置吧
memset(have, , sizeof(have));
memset(c, , sizeof(c));
for(R i = ; i <= n; i++)
{
add(ss, i, p[i]);
t[ * i - ] = r[i], t[ * i] = d[i] + mid;//为离散化做准备
}
sort(t + , t + * n + );//准备离散化了
cnt = , tmp = n;//因为不能和前n个奶酪的编号重了
int a = * n;
t[a + ] = INT_MAX;//不然最后一个点进不来
for(R i = ; i <= a; i++)
if(t[i + ] - t[i] > eps) t[++cnt] = t[i];//去重
for(R i = ; i <= m; i++)//枚举老鼠
{
for(R j = ; j <= cnt; j++)//因为要两个时间点才组成一个时间段
{
++tmp;
add(tmp, tt, i * v[i] * (t[j] - t[j - ]));//连离散化的老鼠到汇点
for(R k = ; k <= n; k++)//枚举奶酪
{
if(r[k] - t[j-] < eps && (d[k] + mid - t[j] > - eps))
add(k, tmp, v[i] * (t[j] - t[j - ]));//连奶酪向老鼠
}//r可以小于t(早就开始了),所以负数也合法,后面是同理的,只是移项了tarjan123 }
}
} void half()
{
ss = * m * n + n + , tt = ss + ;//error!!!ss是要2 * m * n + n + 1啊
while(rr - ll > eps)
{
mid = (rr + ll) / 2.0;
build();
if(bfs() && all - isap() < eps) rr = mid;
else ll = mid;
//printf("%.4lf\n",ans);
//printf("%.4lf %.4lf\n\n",ll,rr);
}
printf("%lf\n", ll);
} void work()
{
T=read();
while(T--)
{
pre();
half();
} } int main()
{
// freopen("in.in","r",stdin);
//freopen("cheese.out","w",stdout);
work();
// fclose(stdin);
//fclose(stdout);
return ;
}
[ZJOI2010]贪吃的老鼠 网络流的更多相关文章
- Luogu2570 [ZJOI2010]贪吃的老鼠 ---- 网络流
Luogu2570 [ZJOI2010]贪吃的老鼠 题面描述 https://www.luogu.org/problemnew/show/P2570 然后题意大概就是m只老鼠,然后吃n个奶酪,已知 ...
- 洛谷$P2570\ [ZJOI2010]$贪吃的老鼠 网络流+二分
正解:网络流+二分 解题报告: 传送门$QwQ$ 和上一题有点儿像,,,?$QwQ$但是比上一题要有趣很多$QwQ$ 首先把大致思路捋下?依然是.二分出每个奶酪的开始和结束时间,然后check下最大流 ...
- [ZJOI2010]贪吃的老鼠(网络流+建图)
题目描述 奶酪店里最近出现了m只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产n块奶酪,其中第i块的大小为pi,会在第ri秒被生产出来,并且必须在第di秒之前将它吃掉.第j只老鼠吃 ...
- Luogu P2570 [ZJOI2010]贪吃的老鼠
Luogu P2570 [ZJOI2010]贪吃的老鼠 题目描述 奶酪店里最近出现了\(m\)只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产\(n\)块奶酪,其中第\(i\)块的 ...
- [ZJOI2010]贪吃的老鼠
很不错的一道网络流的题目 二分答案是显然的 首先不考虑每个饼干只能一个老鼠吃 那很显然的建图就是将时间点按照开始结束的点分成2*n-1段 然后对每一段时间建m个老鼠的点,然后s-它限流,再从它到目前可 ...
- P2570 [ZJOI2010]贪吃的老鼠
传送门 →_→唯一一篇能看得懂的题解---->这里 很容易想到二分+网络流,然而并没有什么卵用--出题人的思路太神了-- 首先考虑如果一块奶酪在同一时间可以被多只老鼠吃的话,该如何建图.首先不难 ...
- Luogu2570 ZJOI2010 贪吃的老鼠 二分答案+最大流
题目链接:https://www.luogu.org/problemnew/show/P2570 题意概述: 好像没什么好概述的.....很简洁? 分析: 首先想到二分时间,转化成判定性问题,在一定时 ...
- 【题解】ZJOI2010贪吃的老鼠
%%%%真的好强...看题解我都看了好久才完全明白.放一下参考的博客,谢谢神犇QAQ 1号博客 2号博客(超级赞的啦) 因为理解的过程太艰辛,所以必须记录一下这道强题:这道题目最难的两个约束就在 ...
- luogu P2570 [ZJOI2010]贪吃的老鼠【二分+最大流】
首先考虑只满足第一个条件,二分答案,把过期时间加上mid之后的2n个时间离散,老鼠拆成每个时间的,第i个时间第j个老鼠为id[i][j],连接(s,i,p[i]),对于离散后时间(g[j-1]~g[j ...
随机推荐
- Redis系列二 Redis数据库介绍
1.SELECT命令 通过查看配置文件可以知道Redis默认有17个库,从0-16. 默认是在0号库.选择库使用SELECT <dbid>命令.例如选择0号库 SELECT 0 2.DB ...
- Linux命令应用大词典-第18章 磁盘分区
18.1 fdisk:分区表管理 18.2 parted:分区维护程序 18.3 cfdisk:基于磁盘进行分区操作 18.4 partx:告诉内核关于磁盘上分区的号码 18.5 sfdisk:用于L ...
- mysql新手进阶02
云想衣裳花想容,春风拂槛露华浓. 若非群玉山头见,会向瑶台月下逢. 现在有一教学管理系统,具体的关系模式如下: Student (no, name, sex, birthday, class) Tea ...
- Win10系统XWware虚拟机安装Linux系统(Ubuntu)最新版教程
XWware虚拟机安装Linux系统(Ubuntu)教程 一.下载并安装VMware虚拟机 借助VMware Workstation Pro, 我们可以在同一台Windows或Linux PC上同时运 ...
- git 跟踪分支 远程跟踪分支 学习笔记
远程跟踪分支相当于一个只读仓库指针,从服务器上获取数据,不可以被本地直接修改. 跟踪分支相当于一个本地指针 用于项目更新和迭代. 1跟踪分支 (tracking branch) 逻辑示意图 ...
- 打包一个Docker镜像,让你的好友加载开启一个容器,并且每隔一秒输出hello,world到指定的文件中
一.两个脚本代码 Dockerfile FROM bash COPY . /usr/herui/ WORKDIR /usr/herui/ CMD [ "sh", "hel ...
- 查找 二叉树中 k1 到 k2区间的节点
vector<int> res; int key1, key2; void traverse(TreeNode * root){//采用前序遍历 if(root == NULL) retu ...
- DeepLearning - Forard & Backward Propogation
In the previous post I go through basic 1-layer Neural Network with sigmoid activation function, inc ...
- JS原型与面向对象总结
ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但 是,ECMAScrip ...
- 《梦断代码Dreaming In Code》阅读笔记(二)
这段时间一口气读了5章,所以想着现在一块写阅读笔记. 在阅读的这段时间,我一直是晚上进行阅读,很多时候都是读完一看已经一个小时了,效果还不错.闲话不表,说说阅读心得. 关于底层语言或是低级语言,我之前 ...