题目链接: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 }