[ZJOI2010]贪吃的老鼠

贪吃的老鼠

题解

首先看到我们是要求的是最小的可以让我们吃完的 T T T,显然这是单调可以二分的。
那我们要考虑如何判断我们在延长 m i d mid mid的情况下是否能够得到一个合法解。
这东西呢,我们可以考虑用网络流求解。
但注意,原问题有要求一只老鼠每个时刻只能吃一块蛋糕,一块蛋糕也只能被一只老鼠吃,这个问题不太好解决。
关于第一个限制,我们可以按时间段拆点,每个时间段给一只老鼠连固定流量的边,保证每只老鼠最多使用这么多流量,一次保证它最多只会吃一块蛋糕。
但第二个条件咋搞?限制蛋糕单位时间总流量?每只老鼠吃的速度不同呀。
事实上我们可以利用差分解决这个问题。
我们先将所有老鼠吃的速度从小到大排序,然后差分,第 i i i个差分段能够对应到第 i i i只到第 n n n只老鼠,所以流入的总流量就是 n − i + 1 n-i+1 ni+1乘上差分段的长度,它向蛋糕单位时间则只会流差分段长度的流量。
我们很容易将这个图映射回每只老鼠的吃法,也一定满足上面的条件,每块蛋糕被吃的总量不会超过最大值,每只老鼠也不会超过它能从的总量。
但是是否是在我们这个单位时间内吃的就很难说了。
不过可以证明一定是可以映射到一组合法解上的。
就按这样跑一遍网络流,判断是否满流即可。

时间复杂度 O ( [ 网 络 流 ] log ⁡ T ) O\left([网络流]\log T\right) O([]logT)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 2005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double Ld;
typedef pair<LL,int> pii;
const int INF=0x3f3f3f3f;
const int mo=19930726;
const int mod=1e6+7;
const int inv2=499122177;
const int inv3=332748118;
const int jzm=2333;
const int zero=2000;
const int n1=100;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double feps=1e-11;
const double eps=1e-9;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0)putchar('-'),print(-x);if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int TT,n,m,s[35],totb,cnt,S,T,head[MAXN],tot,dis[MAXN],cur[MAXN];
double b[65];queue<int>q;
struct ming{int p;double r,d;}a[35];
struct edge{int to,nxt;double flow;int op;}e[MAXN*20];
void addEdge(int u,int v,double f){e[++tot]=(edge){v,head[u],f,0};head[u]=tot;}
void addedge(int u,int v,double f){
	addEdge(u,v,f);e[tot].op=tot+1;
	addEdge(v,u,0);e[tot].op=tot-1; 
}
bool bfs(){
	for(int i=1;i<=cnt;i++)cur[i]=head[i],dis[i]=0;
	while(!q.empty())q.pop();dis[S]=1;q.push(S);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;if(e[i].flow<eps||dis[v])continue;
			dis[v]=dis[u]+1;q.push(v);
			if(v==T)return 1;
		}
	}
	return 0;
}
double dfs(int u,double maxf){
	if(u==T)return maxf;double res=0;
	for(int i=cur[u];i;cur[u]=i=e[i].nxt){
		int v=e[i].to;if(e[i].flow<eps||dis[v]!=dis[u]+1)continue;
		double tmp=dfs(v,min(maxf,e[i].flow));
		maxf-=tmp;res+=tmp;e[i].flow-=tmp;e[e[i].op].flow+=tmp;
		if(maxf<eps)break;
	}
	return res;
}
double dinic(){double res=0;while(bfs())res+=dfs(S,1e9);return res;}
bool check(double mid){
	totb=0;for(int i=1;i<=n;i++)b[++totb]=a[i].r,b[++totb]=a[i].d+mid;
	sort(b+1,b+totb+1);totb=unique(b+1,b+totb+1)-b-1;
	cnt=totb*m+n;S=++cnt;T=++cnt;double summ=0;
	for(int i=1;i<=cnt;i++)head[i]=0;tot=0;
	for(int i=1;i<=n;i++)addedge(i,T,1.0*a[i].p),summ+=a[i].p;
	for(int i=1;i<=totb;i++)
		for(int j=1;j<=m;j++){
			addedge(S,n+(i-1)*m+j,1.0*(m-j+1)*(s[j]-s[j-1])*(b[i]-b[i-1]));
			for(int k=1;k<=n;k++)if(a[k].r<=b[i-1]+eps&&a[k].d+mid+eps>=b[i])
				addedge(n+(i-1)*m+j,k,1.0*(s[j]-s[j-1])*(b[i]-b[i-1]));
		}
	double tmp=dinic();
	return tmp+eps>=summ; 
}
int main(){
	read(TT);
	while(TT--){
		read(n);read(m);
		for(int i=1;i<=n;i++)scanf("%d %lf %lf",&a[i].p,&a[i].r,&a[i].d);
		for(int i=1;i<=m;i++)read(s[i]);sort(s+1,s+m+1);
		double l=0,r=3000000;int times=50;
		while(times--){
			double mid=(l+r)/2.0;
			if(check(mid))r=mid;else l=mid;
		}
		printf("%.4f\n",l);
	}
	return 0;
}

谢谢!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值