题解 P5769 【[JSOI2016]飞机调度】

· · 题解

我爱网络流

刚看到这道题一点思路也没有,后来想一想就想明白了。

首先,为了找出每个航班之间能否转移的关系,我们可以建立一个dis_{i,j}数组,表示从点i到点j并能够再次起飞的最短时间。

由题意,我们可以初始dis_{i,j}=T_{i,j}+P_j,表示一开始的航班都是直飞的。另外,如果i=j,则必有dis_{i,j}=0

然后在dis上跑floyd。这样就完成了dis数组的求解。

如果在一个航班结束后,按照最短路径(即dis),能够按时赶上另一个航班,则它们可以转移。

设航班i是从点x_i到点y_i,时间是从s_it_i,则如果有:

**另,$t_i=s_i+T_{x_i,y_i}+P_{y_i}$,而不是$t_i=s_i+dis_{x_i,y_i}$,因为航班是直飞,不是按照最短路径飞!**~~我为了这个一直WA30。~~ 在转移关系建好之后,我们来观察一下现在的状况: 我们有$m$条航班,航班之间互有有向的转移关系,但转移关系保证无环(因为按时间转移)。 这让人想到了什么? [最小路径覆盖问题](https://www.luogu.com.cn/problem/P2764)!!! ~~不会的人,推一下我的[网络流blog](https://www.luogu.com.cn/blog/Troverld/wang-lao-liu-xue-xi-bi-ji)。第一题就是它。~~ 代码: ```cpp #pragma GCC optimize(3) #include<bits/stdc++.h> using namespace std; int n,m,fix[510],dis[510][510],dd[510][510]; struct AirLine{ int u,v,s,t; }a[510]; int head[1010],cur[1010],dep[1010],cnt,S,T,res; struct node{ int to,next,val; }edge[4010000]; void ae(int u,int v,int 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&&!dep[edge[i].to])dep[edge[i].to]=dep[x]+1,q.push(edge[i].to); } return dep[T]>0; } bool reach; inline int dfs(int x,int flow){ if(x==T){ res+=flow; reach=true; return flow; } int used=0; for(register int &i=cur[x];i!=-1;i=edge[i].next){ if(!edge[i].val||dep[edge[i].to]!=dep[x]+1)continue; register int ff=dfs(edge[i].to,min(edge[i].val,flow-used)); if(ff){ edge[i].val-=ff; edge[i^1].val+=ff; used+=ff; if(used==flow)break; } } return used; } inline void Dinic(){ while(bfs()){ reach=true; while(reach)reach=false,dfs(S,0x3f3f3f3f); } } int main(){ scanf("%d%d",&n,&m),memset(head,-1,sizeof(head)),S=2*m+1,T=2*m+2; for(int i=1;i<=n;i++)scanf("%d",&fix[i]); for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&dd[i][j]),dis[i][j]=(i==j?0:dd[i][j]+fix[j]); for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].s),a[i].t=a[i].s+dd[a[i].u][a[i].v]+fix[a[i].v]; for(int i=1;i<=m;i++)for(int j=1;j<=m;j++)if(a[i].t+dis[a[i].v][a[j].u]<=a[j].s)ae(i,j+m,1); for(int i=1;i<=m;i++)ae(S,i,1),ae(i+m,T,1); Dinic(); printf("%d\n",m-res); return 0; } ```