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

题目链接:https://www.luogu.org/problemnew/show/P2570

 

题意概述:

  好像没什么好概述的.....很简洁?

 

分析:

  首先想到二分时间,转化成判定性问题,在一定时间内可不可以把奶酪吃完。

  对于判定性问题,能不能在限制下达成吃完这个指标,可以想到最大流来解决(求限制下吃蛋糕的最大体积)。

  把蛋糕的生产和过期看成两个事件,可以发现在事件发生的间隔所有老鼠的行为不会有新的选择出现,即能吃的奶酪就那些,问题的性质不发生改变。那么按照事件的发生和结束把事件间隔的时间段离散成一个个点,具体来说这一步是把老鼠拆开了。

    1.每个新点向时间段内存在的每个奶酪连边,容量为inf。

    2.源点向每个老鼠的点连边,容量为si*len,si表示老鼠吃奶酪的速度,len表示时间段的长度。

    3.每个奶酪向汇点连边,容量为奶酪的体积pi,只要奶酪向汇点连的边满流,那么奶酪就是吃完了。

  不难发现这样建图保证了每个老鼠只能在同一时间吃一块奶酪,因为我们限定了老鼠吃奶酪的最大体积,只要吃的体积满足限制,那么就一定不会同一时间吃多块奶酪,反之不合法。

  但是还没有保证一块奶酪只能被一只老鼠吃。此题最妙的地方就在对这里的处理,思考一下我们对上个限制的理解,只要限制了上界,那么就一定可以在合法意义下构造出一个方案!为了构造出合法方案,我们先把所有的速度降序排序,然后进行差分,把每只老鼠的速度变成v[i]=s[i]-s[i+1]。

    1.原点向每只老鼠连边,容量为len*i*v[i]。(这里的限制包含了对ii小于i的老鼠的限制)

    2.每只老鼠向奶酪连边,容量为len*v[i]。

    3.每块奶酪向汇点连边,容量为p[i]。

  解释一下,对于每只老鼠只能吃一块奶酪的限制,我们限制了所有老鼠吃奶酪总量的上界,因此一定可以构造出合法意义下的解。同事对于一个时间段内的奶酪,所有老鼠的速度最大为s[i],两种情况达到最大值都是所有的边满流。

  理解的关键:只要最后最大流满流,显然我们可以找到一种构造流量的方法,把差分之后的速度还原为原来的速度。还原流量的时候从小到大考虑。

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<set>
  9 #include<map>
 10 #include<vector>
 11 #include<cctype>
 12 using namespace std;
 13 const int MAXN=35;
 14 const double eps=1e-7;
 15 
 16 int K,N,M,P[MAXN],R[MAXN],D[MAXN],S[MAXN];
 17 double sump;
 18 struct NET{
 19     static const int maxn=2005;
 20     static const int maxm=120000;
 21     static const double inf=1e9;
 22     struct edge{ int from,to,next; double cap,flow; }E[maxm];
 23     int n,s,t,first[maxn],np,d[maxn],gap[maxn],cur[maxn],fl[maxn];
 24     struct event{
 25         double p; int id;
 26         friend bool operator < (event x,event y){
 27             return x.p<y.p;
 28         }
 29     }e[65]; bool vis[35];
 30     NET(){ n=np=0,s=2001,t=2002; }
 31     void add_edge(int u,int v,double cap){
 32         E[++np]=(edge){u,v,first[u],cap,0};
 33         first[u]=np;
 34         E[++np]=(edge){v,u,first[v],0,0};
 35         first[v]=np;
 36     }
 37     void build(double m){
 38         memset(first,0,sizeof(first));
 39         np=n=0;
 40         for(int i=1;i<=N;i++) add_edge(++n,t,P[i]);
 41         int cnt=0;
 42         for(int i=1;i<=N;i++){
 43             e[++cnt]=(event){R[i],i};
 44             e[++cnt]=(event){D[i]+m,i};
 45         } 
 46         sort(e+1,e+cnt+1);
 47         memset(vis,0,sizeof(vis));
 48         int i=1,k=0;
 49         vis[e[1].id]^=1;
 50         while(i<cnt&&e[i+1].p-e[i].p<eps) vis[e[++i].id]^=1;
 51         double last=e[i++].p;
 52         while(i<=cnt){
 53             for(int j=1;j<=M;j++){
 54                 add_edge(s,n+k*M+j,j*S[j]*(e[i].p-last));
 55                 for(int l=1;l<=N;l++)
 56                     if(vis[l]) add_edge(n+k*M+j,l,S[j]*(e[i].p-last));
 57             }
 58             vis[e[i].id]^=1;
 59             while(i<cnt&&e[i+1].p-e[i].p<eps) vis[e[++i].id]^=1;
 60             k++,last=e[i++].p;
 61         }
 62         n+=k*M+2;
 63     }
 64     void BFS(){
 65         queue<int>q;
 66         for(int i=1;i<=n-2;i++) d[i]=n;
 67         d[s]=d[t]=n;
 68         q.push(t); d[t]=0;
 69         while(!q.empty()){
 70             int i=q.front(); q.pop();
 71             for(int p=first[i];p;p=E[p].next){
 72                 int j=E[p].to,pp=(p-1^1)+1;
 73                 if(d[j]==n&&fabs(E[pp].cap-E[pp].flow)>eps) d[j]=d[i]+1,q.push(j);
 74             }
 75         }
 76     }
 77     double augment(){
 78         int now=t; double flow=inf;
 79         while(now!=s){
 80             flow=min(flow,E[fl[now]].cap-E[fl[now]].flow);
 81             now=E[fl[now]].from;
 82         }
 83         now=t;
 84         while(now!=s){
 85             E[fl[now]].flow+=flow,E[(fl[now]-1^1)+1].flow-=flow;
 86             now=E[fl[now]].from;
 87         }
 88         return flow;
 89     }
 90     double ISAP(){
 91         memcpy(cur,first,sizeof(cur));
 92         memset(gap,0,sizeof(gap));
 93         BFS();
 94         for(int i=1;i<=n-2;i++) gap[d[i]]++;
 95         gap[d[s]]++,gap[d[t]]++;
 96         int now=s; double flow=0;
 97         while(d[s]<n){
 98             if(now==t) flow+=augment(),now=s;
 99             bool ok=0;
100             for(int p=cur[now];p;p=E[p].next){
101                 int j=E[p].to;
102                 if(fabs(E[p].cap-E[p].flow)>eps&&d[j]+1==d[now]){
103                     ok=1,fl[j]=cur[now]=p,now=j;
104                     break;
105                 }
106             }
107             if(!ok){
108                 int minl=n;
109                 for(int p=first[now];p;p=E[p].next){
110                     int j=E[p].to;
111                     if(fabs(E[p].cap-E[p].flow)>eps&&d[j]+1<minl) minl=d[j]+1;
112                 }
113                 if(--gap[d[now]]==0) break;
114                 gap[d[now]=minl]++;
115                 cur[now]=first[now];
116                 if(now!=s) now=E[fl[now]].from;
117             }
118         }
119         return flow;
120     }
121 }net;
122 
123 void data_in()
124 {
125     scanf("%d%d",&N,&M);
126     for(int i=1;i<=N;i++) scanf("%d%d%d",&P[i],&R[i],&D[i]);
127     for(int i=1;i<=M;i++) scanf("%d",&S[i]);
128 }
129 bool check(double mid)
130 {
131     net.build(mid);
132     return fabs(net.ISAP()-sump)<eps;
133 }
134 bool cmp(int x,int y){ return x>y; }
135 void work()
136 {
137     sort(S+1,S+M+1,cmp);
138     for(int i=1;i<M;i++) S[i]=S[i]-S[i+1];
139     sump=0;
140     for(int i=1;i<=N;i++) sump+=P[i];
141     double A=0,B=sump/S[M]+1.0,mid,ans;
142     while(B-A>=eps){
143         mid=(A+B)/2;
144         if(check(mid)) ans=mid,B=mid;
145         else A=mid;
146     }
147     printf("%f\n",ans);
148 }
149 int main()
150 {
151     scanf("%d",&K);
152     for(int i=1;i<=K;i++){
153         data_in();
154         work();
155     }
156     return 0;
157 }
View Code

 

posted @ 2018-03-27 12:02  KKKorange  阅读(264)  评论(0编辑  收藏  举报