题目:
分析:
由数据3得:既然所有人都要学会,肯定是越早学越优。(贪心重要思路)
所以转移就是:dis[v]=max( dis[u] ,L ),u学会之后传授给v的条件是:u先学会,传授的时间在吃饭的时间内
在最短路上转移即可
再考虑有人必须学不会的限制。
如果有一个人u没有学会,就会给他周围的人v一个限制:v不能太早学会,否则吃饭的时候v就会传授给u
所以将lim[v]定为L+1,即他们在L的时候吃饭,L+1的时候v才学会,不会传给u
先将这种传递关系用spfa预处理
再跑一边dij求出每个人最早在多久学会。(将u、v之间的连边关系视作u学会了传授给v来更新v)
转移的时候是这样转移的:dis[v]=max( L,max( dis[u],lim[v] ) )
原因: L指在这个区间内 ,dis指 u要先学会 , lim v 指在v学会的范畴内(防止出现v太早学会而将算法传给后面的人 这就是lim的作用)
#include<bits/stdc++.h> using namespace std; #define N 200005 #define ri register int #define inf 1000000007//一定要足够大 否则会错!! int tot=0,to[N<<1],head[N],nex[N<<1],l[N<<1],r[N<<1],dis[N],vis[N],lim[N],x[N],n,m; struct node1 { int a,b,l,r; }e[N]; struct node2 { int s,dis; }; void add(int a,int b,int ll,int rr) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; l[tot]=ll; r[tot]=rr; to[++tot]=a; nex[tot]=head[b]; head[b]=tot; l[tot]=ll; r[tot]=rr; } bool operator < (const node2 &a,const node2 &b) { return a.dis>b.dis; } void dij()//求u能传给v的dis { priority_queue <node2> q; for(ri i=1;i<=n;++i) dis[i]=inf,vis[i]=0;//dis表示每个人在lim限制下最晚学会的时间 dis[1]=0; q.push((node2){1,0}); while(!q.empty()){ int u=q.top().s; q.pop(); if(vis[u]) continue; vis[u]=1; for(ri i=head[u];i;i=nex[i]){ int v=to[i],mn=l[i],mx=r[i]; int tmp=max(mn,max(dis[u],lim[v]));//u要能在这一次吃饭就传给v 最早吃饭的时间就是从这几个中取max //mn在这个区间内 dis u要先学会 lim v 在v学会的范畴内(防止出现v太早学会而将算法传给后面的人 这就是lim的作用) if(dis[v]>tmp && tmp<=mx){//能更新 并且在范围里面 dis[v]=tmp; if(!vis[v]) q.push((node2){v,dis[v]}); } } } } queue<int> q; void spfa()//求在有lim限制下 u不能传给v的lim { while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(ri i=head[u];i;i=nex[i]){ int v=to[i],mn=l[i],mx=r[i]; if(lim[v]<mn+1 && lim[u]>mx){ //如果说u应该在后面才学会 那么v又在吃饭前学会 说明是矛盾的 所以要将v改成吃完饭后学会 贪心的说就是L+1 lim[v]=mn+1; if(!vis[v]) q.push(v),vis[v]=1; } } } } void print() { printf("Impossible\n"); exit(0); } int main() { freopen("lunch.in","r",stdin); freopen("lunch.out","w",stdout); scanf("%d%d",&n,&m); for(ri i=1;i<=m;++i) scanf("%d%d%d%d",&e[i].a,&e[i].b,&e[i].l,&e[i].r),add(e[i].a,e[i].b,e[i].l,e[i].r); for(ri i=1;i<=n;++i){ scanf("%d",&x[i]); if(x[i]==-1) lim[i]=inf,vis[i]=1,q.push(i);//先将lim的限制传递下去 将与他相连的点的lim 赋成 L+1 else vis[i]=0; } spfa(); dij(); for(ri i=1;i<=n;++i) if( x[i]==1 && dis[i]==inf) print();//如果说是必须学会 但没有学会 是不合法的 for(ri i=1;i<=m;++i){ int u=e[i].a, v=e[i].b; if(x[u]==-1 && dis[v]<=e[i].l) print();//如果说是某一个不能学会 但是却学会了 就是不合法的 if(x[v]==-1 && dis[u]<=e[i].l) print(); } for(ri i=1;i<=m;++i){ int u=e[i].a, v=e[i].b; if(x[u]==-1 || x[v]==-1) printf("%d\n",e[i].l); //如果他们最早不是通过这一次学会的话 这一次就可以直接贪心地直接从l时间吃饭 else printf("%d\n",max(dis[u],dis[v])>e[i].r ? e[i].l : max(e[i].l,max(dis[u],dis[v])) ); //否则就要通过这一次学会 取max是因为要u比v先学会 才能在吃饭的时候传给v } return 0; } /* 4 3 1 2 1 2 2 3 1 2 2 4 1 2 1 0 1 -1 */