题解 P2570 【[ZJOI2010]贪吃的老鼠】
xtx1092515503 · · 题解
这题太神仙了,必须写篇题解记录一下。
首先,一眼能看出的是需要二分保质期
然后就是十分神仙的建图了。
首先,对于时间离散化是必须的。一共
然后,最神仙的一点来了:
我们将老鼠按照速度从大到小排序,并在最后添加一只速度为
举个例子:
这样差分有什么用呢?
题面中有如下限制:
为了满足如上限制,我们进行了拆点。现在我们看看拆点是如何实现如上限制的:
这段是二分的
这个东西的意思是,差分值乘上它的编号。
为什么是这个呢?我们观察一下差分过程。设
则有
或者看图:
我们以上面举的差分例子为例:
这样连的话,就可以保证前面那两条限制。这就相当于在每一横条上取走一部分。如果从竖向来看的话,就是每只老鼠工作一部分。
这样子,我们就知道了为什么要连这么多流量。
之后就比较简单了。
这段是对于时间段
就是这段代码:
for(register int k=1;k<=n;k++)if(((double)s[k]-v[j-1])<EPS&&ip+t[k]-v[j]>-EPS)ae(k,CC,sp[i]*(v[j]-v[j-1]));
然后,最恶心的
1.差分老鼠速度
2.开始二分
2.1.离散化时间点,求出时间段,并由源点连来(奶酪大小)单位的流量
2.2.求出某时间段的某老鼠最多可以贡献多少流量,并向汇点连这么多的流量
2.3.求出所有包含这一时间段的奶酪,并连向该老鼠
2.4.求最大流
以上就是这道神仙又巧妙的题的全过程。希望对你有帮助。
来都来了,点个赞呗~~~
附:注意网络流要写实数网络流,而非整数网络流。
代码:
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
const double EPS=1e-7;
int TT,n,m,sz[50],s[50],t[50],sp[50],sum,CC;
namespace MaxFlow{
const int N=10000,M=200000;
int head[N],cur[N],dep[N],cnt,S,T;
double res;
struct node{
int to,next;
double val;
}edge[M];
inline void ae(int u,int v,double w){
edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=0,head[v]=cnt++;
}
queue<int>q;
inline bool bfs(){
memset(dep,0,sizeof(dep)),q.push(S),dep[S]=1;
while(!q.empty()){
register int x=q.front();q.pop();
for(register int i=cur[x]=head[x];i!=-1;i=edge[i].next)if(edge[i].val>EPS&&!dep[edge[i].to])dep[edge[i].to]=dep[x]+1,q.push(edge[i].to);
}
return dep[T]>0;
}
bool reach;
inline double dfs(int x,double flow){
// printf("%d %lf\n",x,flow);
if(x==T){
res+=flow;
reach=true;
return flow;
}
double used=0;
for(register int &i=cur[x];i!=-1;i=edge[i].next){
if(edge[i].val<EPS||dep[edge[i].to]!=dep[x]+1)continue;
register double ff=dfs(edge[i].to,min(edge[i].val,flow-used));
if(ff>EPS){
edge[i].val-=ff;
edge[i^1].val+=ff;
used+=ff;
if(abs(flow-used)<EPS)break;
}
}
return used;
}
inline void Dinic(){
while(bfs()){
reach=true;
while(reach)reach=false,dfs(S,1e9);
}
}
}
using namespace MaxFlow;
double L,R;
vector<double>v;
bool che(double ip){
memset(head,-1,sizeof(head)),cnt=0,res=0,v.clear();
for(register int i=1;i<=n;i++)ae(S,i,sz[i]),v.push_back(s[i]),v.push_back(ip+t[i]);
sort(v.begin(),v.end()),CC=n;
for(register int i=1;i<=m;i++){
for(register int j=1;j<v.size();j++){
if(v[j]-v[j-1]<EPS)continue;
CC++,ae(CC,T,(v[j]-v[j-1])*sp[i]*i);
for(register int k=1;k<=n;k++)if(((double)s[k]-v[j-1])<EPS&&ip+t[k]-v[j]>-EPS)ae(k,CC,sp[i]*(v[j]-v[j-1]));
}
}
Dinic();
return sum-res<EPS;
}
void solve(){
while(R-L>EPS){
double mid=(L+R)/2;
if(che(mid))R=mid;
else L=mid;
}
}
int main(){
scanf("%d",&TT);
while(TT--){
scanf("%d%d",&n,&m),sum=0,L=R=0,S=2*n*m+n+1,T=2*n*m+n+2;
for(register int i=1;i<=n;i++)scanf("%d%d%d",&sz[i],&s[i],&t[i]),sum+=sz[i];
R=sum;
for(register int i=1;i<=m;i++)scanf("%d",&sp[i]);
sort(sp+1,sp+m+1);
for(register int i=m;i;i--)sp[i]-=sp[i-1];
reverse(sp+1,sp+m+1);
// for(int i=1;i<=m;i++)printf("%d ",sp[i]);puts("");
solve();
printf("%lf\n",L);
}
return 0;
}