P2570 [ZJOI2010]贪吃的老鼠

传送门

→_→唯一一篇能看得懂的题解---->这里

很容易想到二分+网络流,然而并没有什么卵用……出题人的思路太神了……

首先考虑如果一块奶酪在同一时间可以被多只老鼠吃的话,该如何建图。首先不难发现可以把时间离散化代表不同的时间段,然后把每只老鼠按对应的时间拆点。从源点向奶酪连边容量\(p[i]\),然后从奶酪向所有对应它这个时间段的拆出来的老鼠的点连边,容量为\(s[j]\times tim[i]\)(其中\(tim\)表示该段时间的长度),然后从每个老鼠的点向汇点也连这么长的边

然后发现在这种情况下不能保证每块奶酪在同一时间只会被一只老鼠吃……然后考虑一种神奇的方式:将所有老鼠按\(s\)从大到小排序,再令把\(s\)差分一下,即令\(s[i]=s[i]-s[i+1]\),而奶酪向老鼠连的边仍为\(s[j]\times tim[i]\)。那么考虑某一个时间段,某一块奶酪向该时段内这些点的流量和速度的比值分别为\(t[j]\),为了方便起见不妨设\(t[j]\)递增,那么该奶酪流出的总流量为
\[F[i] = \sum_{j = 1} ^ {m-1} ((s[j] - s[j+1]) * t[j]) + s[m] * t[m] = \sum_{j = 2} ^ {m}(s[j] * (t[j] - t[j-1])) + s[1] * t[1]\]
然后发现,该奶酪被吃的总时间是\(t[m]\),又因为流量限制我们可以保证\(t[m]<tim[i]\),且第\(j\)只老鼠吃奶酪的总时间为\(t[j]-t[j-1]\),那么就不会存在奶酪同时被两只老鼠吃的情况了。此时老鼠向汇的边的容量要改成\(j\times s[j]\times tim[i]\),因为在差分之后这些老鼠总的能吃的奶酪应该是\(\sum j\times s[j]\)

//minamoto
#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(register int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define eps 1e-6
#define inf 1e10
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
int read(){
    int res,f=1;char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1e5+5;
struct eg{int v,nx;double w;}e[N<<1];int head[N],tot=1;
void add(int u,int v,double w){
    e[++tot]={v,head[u],w},head[u]=tot;
    e[++tot]={u,head[v],0},head[v]=tot;
}
int n,m,S,T,cur[N],q[N],dep[N];double L,R,mid,sum,p[N],r[N],d[N],tim[N],v[N];
inline bool cmp(double x,double y){return x>y;}
bool bfs(){
    int h,t;fp(i,S,T)dep[i]=-1,cur[i]=head[i];
    q[h=t=1]=S,dep[S]=0;
    while(h<=t){
        int u=q[h++];
        go(u)if(dep[v]<0&&e[i].w>eps){
            dep[v]=dep[u]+1,q[++t]=v;
            if(v==T)return true;
        }
    }
    return false;
}
double dfs(int u,double lim){
    if(u==T||lim<eps)return lim;
    double flow=0,f;
    gg(u)if(dep[v]==dep[u]+1&&(f=dfs(v,min(lim,e[i].w)))>eps){
        flow+=f,lim-=f;
        e[i].w-=f,e[i^1].w+=f;
        if(lim<eps)break;
    }return flow;
}
inline double dinic(){double flow=0;while(bfs())flow+=dfs(S,inf);return flow;}
inline void clr(){tot=1;fp(i,S,T)head[i]=0;}
void solve(){
    int cnt;double flow;S=0,T=2*n*m+n+1;
    while(R-L>eps){
        mid=(L+R)/2,clr();
        fp(i,1,n)add(S,i,p[i]),tim[2*i-1]=r[i],tim[2*i]=d[i]+mid;
        sort(tim+1,tim+2*n+1),cnt=n;
        fp(i,1,m)fp(j,2,2*n)if(tim[j]-tim[j-1]>eps){
            add(++cnt,T,i*v[i]*(tim[j]-tim[j-1]));
            fp(k,1,n)if(r[k]-tim[j-1]<eps&&(d[k]+mid-tim[j])>-eps)
            add(k,cnt,v[i]*(tim[j]-tim[j-1]));
        }flow=dinic();
        sum-flow<eps?R=mid:L=mid;
    }printf("%lf\n",L);
}
int main(){
//  freopen("testdata.in","r",stdin);
    int times=read();
    while(times--){
        sum=0,n=read(),m=read();
        fp(i,1,n)p[i]=read(),r[i]=read(),d[i]=read(),sum+=p[i];
        fp(i,1,m)v[i]=read();
        sort(v+1,v+1+m,cmp),R=sum/v[1]+1,L=0;
        fp(i,1,m-1)v[i]-=v[i+1];solve();
    }return 0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10040884.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值