目录
1.Sightseeing Trip
思路借鉴:传送门
代码参考于:传送门
设环的形式是:i<->k<->j , i<-->j (i,j,k不同)
floyd是典型的插点算法,每次插入点k,为此,在点k被[插入前]可计算i-j-k这个环
即此时中间节点为:1~k-1,即我们已经算出了任意i<->j的最短道路,中间经过的节点可以为 (1,2,3,...,k-1)
我们只需枚举所有以k为环中最大节点的环即可。
pos[i][j]:i~j的最短路中经过的点是k(即由这个状态转移过来),且这个k是此路径中编号最大的点(除i,j)//根据Floyd算法实质决定
这条道路存在以下两条性质
1.在i~j的最短道路中,一定没有环(显然)
2.设i,j之间的最短道路经过点k(不同于i,j),则i~k , k~j之间必然没有交集
2证:
如果有交集,设交点为k'(k' < k,根据Floyd算法实质相关),则存在道路:
i<->k'<->j , 由于[i<->k'] < [i<->k] , [j->k'] < [j->k]
显然这条道路更小,和假设矛盾所以一定没有交集
对于pos[i][j],如果pos[i][j] == 0 : 说明i~j的最短路没有经过其他节点
因此借用性质2来求解道路,注意书写顺序,确保最后输出顺序正确
每次把i <-> j 之间划分成 i<->k , k<->j
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110,INF=0x3f3f3f3f;
int n,m;
int pos[N][N],d[N][N],g[N][N];
int path[N],cnt;
int res=INF;
void get_path(int i,int j)//获取i~j中由哪个点转移过来的,i~k,k~j这样子
{
int k=pos[i][j];
if(!k) return;//假如没有
//这里遍历的顺序随意,因为有Special judge
get_path(i,k);
path[cnt++]=k;
get_path(k,j);
}
void floyd()
{
for(int k=1;k<=n;k++)//dp思路, 假设k是环上的最大点, i ~ k ~ j(Floyd的思想)
{
for(int i=1;i<k;i++)// 至少包含三个点,i,j,k不重合
for(int j=i+1;j<k;j++)
if((ll)d[i][j]+g[i][k]+g[k][j]<res)
{
res=d[i][j]+g[i][k]+g[k][j];//更新环的最小值
cnt=0;
//把路径上的点存下来,这里随便顺序,因为有Special judge
path[cnt++]=i;
path[cnt++]=k;
get_path(i,j);
path[cnt++]=j;
}
for(int i=1;i<=n;i++)//正常的Floyd更新最短距离
for(int j=1;j<=n;j++)
if(d[i][j]>d[i][k]+d[k][j])
{
d[i][j]=d[i][k]+d[k][j];
pos[i][j]=k;//记录由那个点更新过来的
}
}
}
int main()
{
memset(d,0x3f,sizeof d);
scanf("%d%d",&n,&m);
int a,b,c;
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
d[a][b]=d[b][a]=min(d[a][b],c);
}
memcpy(g,d,sizeof d);
floyd();//跑一遍Floyd算法求路径最小环
if(res==INF) puts("No solution");//假如无环
else
{
for(int i=0;i<cnt;i++) printf("%d ",path[i]);
puts("");
}
return 0;
}
2.拯救大兵瑞恩
题意就是从左上角到右下角的最短距离,但是跟走迷宫不太一样,他有门的限制,所以要先开门得拿到相应的钥匙才行,走一步的消耗是1点体力
我们可以采用状态分析的方式进行考虑,d[x,y,state]所有从起点走到(x,y)这个格子且拥有的钥匙的状态是state的所有路线的集合
但是由于这里可能会有环 没有拓扑序 实际不能用dp做 只能转换为最短路
把两种转移方式看成两种边
因为这里只有0和1的边所以可以用双端队列广搜来做,然后答案就是d[n,m,0~2^p-1]
然后我们可以用一维的坐标边数两维的位置
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int N=11,M=N*N,E=400,P=1<<10;
int h[M],e[E],ne[E],w[E],idx;//w记录的是门需要的钥匙
int n,m,p,k;
int g[N][N],key[M];
int dist[M][P];
bool st[M][P];
set<pii> edges;//用来记录出现过的边
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void built()
{
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int u=0;u<4;u++)//枚举四个方向
{
int x=i+dx[u],y=j+dy[u];
if(x<=0||x>n||y<=0||y>m) continue;//越界
int a=g[i][j],b=g[x][y];
if(edges.count({a,b})==0) add(a,b,0);//假如不是门或者墙,则说明不用钥匙也能过去,即0就是不需要钥匙
}
}
int bfs()//双端队列广搜
{
memset(dist,0x3f,sizeof dist);
dist[1][0]=0;//从起点走,钥匙的状态是0
deque<pii> q;
q.push_back({1,0});
while(q.size())
{
pii t=q.front();
q.pop_front();
if(st[t.x][t.y]) continue;
st[t.x][t.y]=true;
if(t.x==n*m) return dist[t.x][t.y];//假如走到了终点直接返回
if(key[t.x])//假如这个位置有钥匙
{
int state=t.y|key[t.x];//把这个钥匙的状态加上
if(dist[t.x][state]>dist[t.x][t.y])
{
dist[t.x][state]=dist[t.x][t.y];//更新最短距离
q.push_front({t.x,state});//因为边权是0,则加到队头中去
}
}
for(int i=h[t.x];~i;i=ne[i])//枚举领边
{
int j=e[i];
if(w[i]&&!(t.y>>w[i]-1&1)) continue;//假如这个位置有钥匙但是开不了门
if(dist[j][t.y]>dist[t.x][t.y]+1)//更新最短距离
{
dist[j][t.y]=dist[t.x][t.y]+1;
q.push_back({j,t.y});//因为边权是1则加到队尾
}
}
}
return -1;//反之无解
}
int main()
{
scanf("%d%d%d%d",&n,&m,&p,&k);
for(int i=1,t=1;i<=n;i++)//将二维坐标映射到一维的点中
for(int j=1;j<=m;j++)
g[i][j]=t++;
memset(h,-1,sizeof h);
while(k--)
{
int x1,y1,x2,y2,c;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
int a=g[x1][y1],b=g[x2][y2];
edges.insert({a,b}),edges.insert({b,a});//标记这两条边有门或者墙
if(c) add(a,b,c),add(b,a,c);//加入是门,则这条边加上这个门需要的钥匙
}
built();//建立其他点之间的边权
int s;
scanf("%d",&s);
while(s--)//读入钥匙
{
int x,y,id;
scanf("%d%d%d",&x,&y,&id);
key[g[x][y]]|=1<<id-1;//将这个位置的钥匙的状态存下来
}
printf("%d\n",bfs());
return 0;
}
3.架设电话线
看看别人的题解:传送门
二分价格最贵的那根线的费用,然后进行判断是否满足所选出的边小于等于K
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,M=4e3+10;
int h[N],ne[M],e[M],w[M],idx;
int n,p,k;
bool st[N];
int dist[N];
deque<int> q;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool bfs(int mid)
{
//清空上一层状态
q.clear();
memset(st,0,sizeof st);
memset(dist,0x3f,sizeof dist);
q.push_back(1);
dist[1]=0;
while(q.size())
{
int t=q.front();
q.pop_front();
if(st[t]) continue;
st[t]=true;
for(int i=h[t];~i;i=ne[i])
{
int j=e[i],d=w[i]>mid;//d是判断这条边是否大于mid
if(dist[j]>dist[t]+d)//假如可以更新最少的边
{
dist[j]=dist[t]+d;
if(d) q.push_back(j);//假如是0则假如队头
else q.push_front(j);//反之加到队尾
}
}
}
return dist[n]<=k;//看看所需的边是否小于等于k条
}
void solve()
{
memset(h,-1,sizeof h);
cin>>n>>p>>k;
int a,b,c;
while(p--)
{
cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
int l=0,r=1000001;
while(l<r)//二分费用
{
int mid=l+r>>1;
if(bfs(mid)) r=mid;
else l=mid+1;
}
if(l==1000001) puts("-1");
else cout<<l<<endl;
}
int main()
{
int T=1;
while(T--) solve();
return 0;
}
4.农场派对
这题就是正向求一遍最短路和逆向求一遍最短路,然后更新每头牛的最大值
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,M=2e5+10;
int h1[N],ne1[M],e1[M],w1[M],idx1;
int h2[N],ne2[M],e2[M],w2[M],idx2;
int n,m,x;
bool st[N];
int dist1[N],dist2[N];
int q[N];
void add1(int a,int b,int c)
{
e1[idx1]=b,w1[idx1]=c,ne1[idx1]=h1[a],h1[a]=idx1++;
}
void add2(int a,int b,int c)
{
e2[idx2]=b,w2[idx2]=c,ne2[idx2]=h2[a],h2[a]=idx2++;
}
void spfa(int h[],int e[],int w[],int ne[],int dist[],int type)//spfa求最短路
{
memset(st,0,sizeof st);
if(type==1) memset(dist,0x3f,sizeof dist1);
else memset(dist,0x3f,sizeof dist2);
int hh=0,tt=0;
q[tt++]=x;
dist[x]=0;
while(hh!=tt)
{
int t=q[hh++];
st[t]=false;
if(hh==N) hh=0;
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
if(!st[j])
{
q[tt++]=j;
st[j]=true;
if(tt==N) tt=0;
}
}
}
}
}
void solve()
{
memset(h1,-1,sizeof h1);
memset(h2,-1,sizeof h2);
cin>>n>>m>>x;
int a,b,c;
while(m--)
{
cin>>a>>b>>c;
add1(a,b,c);
add2(b,a,c);
}
spfa(h1,e1,w1,ne1,dist1,1);//正向求一遍最短路
spfa(h2,e2,w2,ne2,dist2,2);//逆向求一遍最短路
int res=0;
for(int i=1;i<=n;i++) res=max(res,dist1[i]+dist2[i]);//求每头牛来回的最大值
cout<<res<<endl;
}
int main()
{
int T=1;
while(T--) solve();
return 0;
}
5.Roadblocks
这题求次短路,在求最短路的时候顺便更新最短路即可
1.假如最短路可以更新,则次短路等于最短路的值,最短路更新为最小的值
2.假如次短路可以更新, 并且最短路是小于更新的值的,则更新次短路的值
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10,M=2e5+10;
int h[N],e[M],ne[M],w[M],idx;
int d1[N],d2[N];//d1是最短路,d2是次短路
int n,m;
typedef struct
{
int u,dist;
}pii;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void bfs()
{
memset(d1,0x3f,sizeof d1);
memset(d2,0x3f,sizeof d2);
queue<pii> q;
d1[1]=0;
q.push({1,0});
while(q.size())
{
pii t=q.front();
q.pop();
int u=t.u,dist=t.dist;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(d1[j]>dist+w[i])//假如可以更新最短路
{
d2[j]=d1[j];//次短路等于最短路
d1[j]=dist+w[i];//最短路更新为最小值
q.push({j,d1[j]});
}
else if(d1[j]<dist+w[i]&&d2[j]>dist+w[i])//假如可以更新次短路,并且最短路严格小于更新的值
{
d2[j]=dist+w[i];//更新次短路
q.push({j,d2[j]});
}
}
}
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
int a,b,c;
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
bfs();
printf("%d\n",d2[n]);//输出次短
return 0;
}
6.最短路计数
假如在更新最短路时,某个点j可以由某个点t更新过来,则j这个点的最短路个数cnt[j]=cnt[t],假如最短路的长度相等,则直接cnt[j]+=cnt[t].
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=4e5+10,mod=100003;
int h[N],e[M],ne[M],idx;
int q[N];
int n,m;
int dist[N],cnt[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
int hh=0,tt=0;
q[0]=1;
cnt[1]=1;//起点到起点最短路是1条
while(hh<=tt)
{
int t=q[hh++];
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+1)//假如可以更新最短路
{
dist[j]=dist[t]+1;
cnt[j]=cnt[t];//则最短路数等于更新过来的那个数
q[++tt]=j;
}
else if(dist[j]==dist[t]+1) cnt[j]=(cnt[j]+cnt[t])%mod;//假如相等则加上
}
}
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
int a,b;
while(m--)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
bfs();//用bfs求最短路
for(int i=1;i<=n;i++) printf("%d\n",cnt[i]%mod);//输出每个点的最短路数
return 0;
}
7.新年好
先两两求一遍最短路,求一个地方到另一个地方的最短路,在枚举5个拜访的顺序
由于有5个亲戚,先预处理出来5个亲戚到其他所有点的最短距离待会可以直接用,则可以dfs的全排列方式来进行拜访顺序,在把经过的点加上相应的距离即可,最后更新一遍最小值
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,M=2e5+10;
int n,m;
int h[N],e[M],w[M],ne[M],idx;
bool st[N];
int ans=0x3f3f3f3f;
int dist[10][N];
int q[N];
int id[10];//存每个亲戚所在的站点
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(int u,int dist[])//spfa求最短路
{
memset(st,0,sizeof st);
int hh=0,tt=0;
q[tt++]=u;
dist[u]=0;
while(hh!=tt)
{
int t=q[hh++];
if(hh==N) hh=0;
st[t]=false;
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
if(!st[j])
{
q[tt++]=j;
if(tt==N) tt=0;
st[j]=true;
}
}
}
}
}
void dfs(int cnt,int u,int sum)//枚举到u这个点,cnt个数,最短花费为sum
{
if(sum>=ans) return;//加入大于最短了
if(cnt==6)//加入枚举完5个亲戚了
{
ans=sum;
return;
}
for(int i=2;i<=6;i++)//全排列枚举5个亲戚
if(!st[i])
{
st[i]=true;
dfs(cnt+1,i,sum+dist[u][id[i]]);
st[i]=false;
}
}
void solve()
{
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
id[1]=1;//自己在1站点
for(int i=2;i<=6;i++) scanf("%d",&id[i]);
memset(dist,0x3f,sizeof dist);
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
for(int i=1;i<=6;i++) spfa(id[i],dist[i]);//求每个亲戚到其他点的最短距离
memset(st,0,sizeof st);
dfs(1,1,0);//枚举搜索顺序,全排列
printf("%d\n",ans);
}
int main()
{
int T=1;
while(T--) solve();
return 0;
}
8.最优贸易
DP+spfa求最短路
dmin[i]表示1~i中的最小价值,dmax[i]表示i~n中的最大价值
则我们可以在前i个最小值入手,在后i个抛售,则获利为dmax[i]-dmin[i],然后求一遍最大即可
这题因为存在环不可用dijkstra来做
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=1e6+10;
int hs[N],ht[N],e[M],ne[M],w[N],idx;
int dmax[N],dmin[N];
int n,m;
int q[N];
bool st[N];
void add(int h[],int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void spfa(int h[],int type)
{
int hh=0,tt=1;
if(type)//求最大值的情况
{
memset(dmax,-0x3f,sizeof dmax);
q[0]=n;
dmin[n]=w[n];
}
else//求最小值的情况
{
memset(dmin,0x3f,sizeof dmin);
q[0]=1;
dmax[1]=w[1];
}
while(hh!=tt)
{
int t=q[hh++];
if(hh==N) hh=0;
st[t]=false;
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(type&&dmax[j]<max(dmax[t],w[j])||!type&&dmin[j]>min(dmin[t],w[j]))//假如最大值或者最小值可以更新
{
if(type) dmax[j]=max(dmax[t],w[j]);//假如最大值则更新最大值
else dmin[j]=min(dmin[t],w[j]);//反之更新最小值
if(!st[t])
{
q[tt++]=j;
if(tt==N) tt=0;
st[j]=true;
}
}
}
}
}
int main()
{
memset(hs,-1,sizeof hs);
memset(ht,-1,sizeof ht);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
int a,b,c;
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
add(hs,a,b),add(ht,b,a);//建一条正向的供求1~i中的最小用,建一条反向的供n~i中的求最大用
if(c==2) add(hs,b,a),add(ht,a,b);//假如是双向边,则两个都建
}
spfa(hs,0);//求1~i中的最小值
spfa(ht,1);//求n~i中的最大值
int res=0;
for(int i=1;i<=n;i++) res=max(res,dmax[i]-dmin[i]);//求一遍最大值
printf("%d\n",res);
return 0;
}
9.汽车加油行驶问题
借鉴于:传送门
这题是假的网络流,虽然说费用流也不是不可以写。。。
设disx,y,kdisx,y,k表示到达位置在(x,y)(x,y),剩余油量为kk这个状态的最小代价。可知这个东西可以用SPFASPFA转移。
所以转移即可。
注意:题目中说经过油库时若油箱不满是强制加油的。没判这个就要WA两个点(而且跑出来答案小了)
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,K,A,B,C;
int mp[N][N];
int dist[N][N][11];//表示走到x,y这个点剩余步数是k的最小花费
bool st[N][N][11];
int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};//左上右下
typedef struct
{
int x,y,k;
}pii;
queue<pii> q;
void spfa()
{
memset(dist,0x3f,sizeof dist);
dist[1][1][K]=0,q.push({1,1,K});//把起点放进队列中
while(q.size())
{
pii t=q.front();
q.pop();
int x=t.x,y=t.y,k=t.k;
st[x][y][k]=false;
if(mp[x][y]&&k<K)//假如是加油站而且油量不够满
{
if(dist[x][y][K]>dist[x][y][k]+A)//假如可以加油
{
dist[x][y][K]=dist[x][y][k]+A;
if(!st[x][y][K]) q.push({x,y,K}),st[x][y][K]=true;
}
continue;//表示加油站强制加油,则后面就不用更新了
}
else
{
if(dist[x][y][K]>dist[x][y][k]+A+C)//表示新设立一个加油站,并加油
{
dist[x][y][K]=dist[x][y][k]+A+C;
if(!st[x][y][K]) q.push({x,y,K}),st[x][y][K]=true;
}
}
if(k)//假如有油,则可以更新四个方向的最小值
{
for(int i=0;i<4;i++)
{
int tx=x+dx[i],ty=y+dy[i];
if(x<1||x>n||y<1||y>n) continue;//越界
if(i<=1&&dist[tx][ty][k-1]>dist[x][y][k]+B)//假如倒着走,则得加上费用B,而且能更新最小值
{
dist[tx][ty][k-1]=dist[x][y][k]+B;
if(!st[tx][ty][k-1]) q.push({tx,ty,k-1}),st[tx][ty][k-1]=true;
}
if(i>1&&dist[tx][ty][k-1]>dist[x][y][k])//正着走,不用加费用,而且能更新最小值
{
dist[tx][ty][k-1]=dist[x][y][k];
if(!st[tx][ty][k-1]) q.push({tx,ty,k-1}),st[tx][ty][k-1]=true;
}
}
}
}
}
int main()
{
scanf("%d%d%d%d%d",&n,&K,&A,&B,&C);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&mp[i][j]);
spfa();//跑一遍spfa求最小花费
int ans=dist[n][n][0];
for(int i=1;i<=K;i++) ans=min(ans,dist[n][n][i]);//更新最后一个点剩余i步是最小花费
printf("%d\n",ans);
return 0;
}
10.道路和航线
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> pii;
const int N=25010,M=150010;
int h[N],e[M],ne[M],w[M],idx;
int n,m1,m2,s;
int id[N],bcnt,din[N];//id是由点找连通块,bcnt是连通块的个数,din表示某个连通块的入度
int dist[N];
bool st[N];
vector<int> block[N];//存某个连通块中的点
queue<int> q;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int ver)//当前点为u,当前连通块是ver
{
block[ver].push_back(u);//放进连通块中
id[u]=ver;//标记这个点属于那个连通块
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(!id[j]) dfs(j,ver);//假如没搜索过
}
}
void dijkstra(int ver)
{
priority_queue<pii,vector<pii>,greater<pii>> heap;
for(int t:block[ver]) heap.push({dist[t],t});//把连通块中的所有点加入堆中
while(heap.size())
{
pii t=heap.top();
heap.pop();
int u=t.y,d=t.x;
if(st[u]) continue;
st[u]=true;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(dist[j]>d+w[i])
{
dist[j]=d+w[i];
if(id[j]==id[u]) heap.push({dist[j],j});//加入是同一个连通块则继续加入堆中更新
}
if(id[j]!=id[u]&&--din[id[j]]==0) q.push(id[j]);//加入不是一个连通块并且入度减为0了,则把这个连通块加入队列中
}
}
}
void topsort()
{
memset(dist,0x3f,sizeof dist);
dist[s]=0;
for(int i=1;i<=n;i++)//把所有入度为0的连通块加入队列中
if(!din[i]) q.push(i);
while(q.size())
{
int t=q.front();
q.pop();
dijkstra(t);//连通块内部跑一遍dijkstra
}
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d%d%d",&n,&m1,&m2,&s);
int a,b,c;
while(m1--)//道路
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
for(int i=1;i<=n;i++)//获取连通块,即互相能到达的点为1个连通块
if(!id[i])//假如没有搜索过
dfs(i,++bcnt);//开辟新的连通块
while(m2--)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
din[id[b]]++;//表示b这个连通块的入度++
}
topsort();//跑一遍拓扑序
for(int i=1;i<=n;i++)
if(dist[i]>=0x3f3f3f3f/2) puts("NO PATH");//假如数比较大,说明没有路径
else printf("%d\n",dist[i]);
return 0;
}