~~~题面~~~

题解:

这是一道强题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]贪吃的老鼠 网络流的更多相关文章

  1. Luogu2570 [ZJOI2010]贪吃的老鼠 ---- 网络流

    Luogu2570  [ZJOI2010]贪吃的老鼠 题面描述 https://www.luogu.org/problemnew/show/P2570 然后题意大概就是m只老鼠,然后吃n个奶酪,已知 ...

  2. 洛谷$P2570\ [ZJOI2010]$贪吃的老鼠 网络流+二分

    正解:网络流+二分 解题报告: 传送门$QwQ$ 和上一题有点儿像,,,?$QwQ$但是比上一题要有趣很多$QwQ$ 首先把大致思路捋下?依然是.二分出每个奶酪的开始和结束时间,然后check下最大流 ...

  3. [ZJOI2010]贪吃的老鼠(网络流+建图)

    题目描述 奶酪店里最近出现了m只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产n块奶酪,其中第i块的大小为pi,会在第ri秒被生产出来,并且必须在第di秒之前将它吃掉.第j只老鼠吃 ...

  4. Luogu P2570 [ZJOI2010]贪吃的老鼠

    Luogu P2570 [ZJOI2010]贪吃的老鼠 题目描述 奶酪店里最近出现了\(m\)只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产\(n\)块奶酪,其中第\(i\)块的 ...

  5. [ZJOI2010]贪吃的老鼠

    很不错的一道网络流的题目 二分答案是显然的 首先不考虑每个饼干只能一个老鼠吃 那很显然的建图就是将时间点按照开始结束的点分成2*n-1段 然后对每一段时间建m个老鼠的点,然后s-它限流,再从它到目前可 ...

  6. P2570 [ZJOI2010]贪吃的老鼠

    传送门 →_→唯一一篇能看得懂的题解---->这里 很容易想到二分+网络流,然而并没有什么卵用--出题人的思路太神了-- 首先考虑如果一块奶酪在同一时间可以被多只老鼠吃的话,该如何建图.首先不难 ...

  7. Luogu2570 ZJOI2010 贪吃的老鼠 二分答案+最大流

    题目链接:https://www.luogu.org/problemnew/show/P2570 题意概述: 好像没什么好概述的.....很简洁? 分析: 首先想到二分时间,转化成判定性问题,在一定时 ...

  8. 【题解】ZJOI2010贪吃的老鼠

    %%%%真的好强...看题解我都看了好久才完全明白.放一下参考的博客,谢谢神犇QAQ 1号博客    2号博客(超级赞的啦) 因为理解的过程太艰辛,所以必须记录一下这道强题:这道题目最难的两个约束就在 ...

  9. luogu P2570 [ZJOI2010]贪吃的老鼠【二分+最大流】

    首先考虑只满足第一个条件,二分答案,把过期时间加上mid之后的2n个时间离散,老鼠拆成每个时间的,第i个时间第j个老鼠为id[i][j],连接(s,i,p[i]),对于离散后时间(g[j-1]~g[j ...

随机推荐

  1. Redis系列二 Redis数据库介绍

    1.SELECT命令 通过查看配置文件可以知道Redis默认有17个库,从0-16. 默认是在0号库.选择库使用SELECT <dbid>命令.例如选择0号库  SELECT 0 2.DB ...

  2. Linux命令应用大词典-第18章 磁盘分区

    18.1 fdisk:分区表管理 18.2 parted:分区维护程序 18.3 cfdisk:基于磁盘进行分区操作 18.4 partx:告诉内核关于磁盘上分区的号码 18.5 sfdisk:用于L ...

  3. mysql新手进阶02

    云想衣裳花想容,春风拂槛露华浓. 若非群玉山头见,会向瑶台月下逢. 现在有一教学管理系统,具体的关系模式如下: Student (no, name, sex, birthday, class) Tea ...

  4. Win10系统XWware虚拟机安装Linux系统(Ubuntu)最新版教程

    XWware虚拟机安装Linux系统(Ubuntu)教程 一.下载并安装VMware虚拟机 借助VMware Workstation Pro, 我们可以在同一台Windows或Linux PC上同时运 ...

  5. git 跟踪分支 远程跟踪分支 学习笔记

    远程跟踪分支相当于一个只读仓库指针,从服务器上获取数据,不可以被本地直接修改. 跟踪分支相当于一个本地指针   用于项目更新和迭代. 1跟踪分支  (tracking  branch)   逻辑示意图 ...

  6. 打包一个Docker镜像,让你的好友加载开启一个容器,并且每隔一秒输出hello,world到指定的文件中

    一.两个脚本代码 Dockerfile FROM bash COPY . /usr/herui/ WORKDIR /usr/herui/ CMD [ "sh", "hel ...

  7. 查找 二叉树中 k1 到 k2区间的节点

    vector<int> res; int key1, key2; void traverse(TreeNode * root){//采用前序遍历 if(root == NULL) retu ...

  8. DeepLearning - Forard & Backward Propogation

    In the previous post I go through basic 1-layer Neural Network with sigmoid activation function, inc ...

  9. JS原型与面向对象总结

    ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但 是,ECMAScrip ...

  10. 《梦断代码Dreaming In Code》阅读笔记(二)

    这段时间一口气读了5章,所以想着现在一块写阅读笔记. 在阅读的这段时间,我一直是晚上进行阅读,很多时候都是读完一看已经一个小时了,效果还不错.闲话不表,说说阅读心得. 关于底层语言或是低级语言,我之前 ...