这是一篇错误的题解,但是注释是对的,又一个地方不会,
我先搁置
/*
竟然是把每一辆车抽象成一个点,有疫苗的车向没有疫苗的车传送疫苗
看成他们两个之间有一个有向边,但是这个有向边的长度是不断变化的,
这两个车必须在同一个时间经过同样的点,这样才能把疫苗传送
假设i号车向j号车传送疫苗,i号车可能在这一圈上的多个点上接到疫苗,
接到疫苗的时候,j也可能在j所在的圈上的任何一个点上
用dist[i]存放i号车拿到疫苗的最早时刻, 假设i号车最早在圈上的一个点p
拿到疫苗,,假设从p到i号圈和j号圈的交点为q,假设从p到q的时间为a,一圈的长度为b
则i号车,在a+kb的时间时,会在p点,现在再看一下j号车,在哪些时刻会到q点?
由于输入时,保证了一圈上的所有点都是不同的,假设当i号车拿到疫苗时,j号车在
他所在的圈上的f点,那么假设f到q的时间为x,j号车一圈的时间为y,则j号车道q点的时刻为
x+yt, 所以如果想要交接疫苗,就得让两个时间相等,即a+bk=x+yt, 求的就是这个k的最小非负整数解
这是一个不定方程,有两个未知数k和t都在变换,我们把变量移到左边:bk-yt=x-a, 求最小的k,不需要管t的值
基础课中的扩展欧几里得算法,求解线性同余方程,bk=(x-a)% y
如何快速判断两条路线是否相交, ps[i]存放所有经过点i的路线,要用结构体,首先存一下圈的id,然后这个点在环线上
到起点的距离sum,同时存一下这个点是环上的第几个点pid
这道题用了dijkstra,时间复杂度是n方的,n是点数,为什么没用堆优化版的?
因为堆优化版的时间复杂度是mlongn, m是边数,在这道题中,最多有500辆车,
没两辆车之间最多有500个交点,所以m最大是500的三次方,实际比较一下,堆优化版的还
不如朴素的dijkstra呢
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N=510; //车的数量
const LL INF = 0x3f3f3f3f3f3f3f3fll;//ll型的有8个
int n, m;
int len[N];//每条路线的总长度
struct Node{
int cid; //圈的id
int sum; //到这个圈上起点的距离
int pid; //是这个圈上的第几个点
};
int pid[N];//pid存放j号路线上最早拿到疫苗,是在哪个点拿到的
vector<Node> ps[N]; //ps[i]存放经过i号点的圈
vector<PII> line[N];// x表示节点的编号, y表示到下一个点的距离
LL dist[N]; //每辆车最早拿到疫苗的时间
LL ans[N];//每个点最早拿到疫苗的时间
bool st[N]; //dij用到的判重数组
LL exgcd(int a, int b, LL& x, LL& y)
{
if(!b)
{
x=1, y=0;
return a;
}
LL d=exgcd(b, a%b, y, x);
y-=(a/b) * x;
return d;
}
//朴素版
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
//判断一下哪些点可以经过1号点,如果可以的话,他就可以在1号点拿到疫苗
for(int i=0; i<m; i++)
{
int d=0;
for(int j=0; j<line[i].size(); j++)
{
if(line[i][j].x == 1)
{
dist[i] = d;//拿到疫苗的时间就是d
pid[i]=j;
break;
}
d+=line[i][j].y;
}
}
//做m次
for(int i=0; i<m; i++)
{
int t=-1;
for(int j=0; j<m; j++)
if(!st[j] && (t == -1 || dist[j] < dist[t])) //如果这个点没有被搜过,并且t没有赋过值,或者j更小
t=j;
st[t]=true;
//用t去更新其他点
auto&l =line[t];//环上的所有点
auto d=dist[t];//t最早拿到疫苗的时间
//枚举一下当前环上的所有点,再去枚举这个点上的所有其他环
for(int j=pid[t], k=0; k<l.size(); j=(j+1)mod l.size, k++)
{
//枚举这个点上的所有环
for(auto& c:ps[l[j].x]) //c是node类型的,
{
if(st[c.cid]) continue;//如果这个环被算过了,过,这个优化很重要!
//否则,把a和b都求出来
LL a=d, b=len[t];
LL x=c.sum;//这里改成x=len[c.cid]-c.sum也是对的
y=len[c.cid];
//k用X来表示, t用Y来表示
LL X, Y;
// 这个函数可以求出来ax+by=gcd(a,b)中的x和y,
// 并且返回a和b的最大公因数
LL D = exgcd(b, y, X, Y);
//如果这个不定方程无解
if((x-a) % D) continue;
X=(x-a) / D *X;//扩展(x-a) / D 倍, k求出来了
y %=D; //这里还是没搞明白
//求一下X%Y的非负余数
X=(X % y + y) % y;
if( dist[c.cid] > a+b*X)
{
dist[c.cid]=a+b*X;
// pid存放j号路线上最早拿到疫苗,是在哪个点拿到的
pid[c.cid]=c.pid;
}
}
d+=l[j].y;
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=0; i<m; i++)//m条路线
{
int cnt, sum=0; //路线上点的数量和路线上的点到起点的距离
scanf("%d", &cnt);
for(int j=0; j<cnt; j++)
{
int ver, t;
scanf("%d%d", &ver, &t);
ps[ver].push_back({i, sum, j});//存一下是第几条路线,到起点的距离还有是这条路线上的第几个点
line[i].push_back({ver, t});
sum+=t; //表示下个点到起点的距离多了一个t
}
len[i]=sum;//第i条路线的总长度
}
dijkstra();//求出来所有的dist[i]
memset(ans, 0x3f, sizeof ans);
//枚举所有的环线,去更新所有的点
for(int i=0; i<m; i++)
{
if(dist[i] == INF) continue;
//如果不是的话,用这个路线更新一下
//如果用这辆车来运的话,到达其他点的最早时间
LL d=dist[i];
//pid存放j号路线上最早拿到疫苗,是在哪个点拿到的
for(int j=pid[i], k=0; k<line[i].size(); j=(j+1)mod line[i].size(), k++)
{
int ver = line[i][j].x; //存当前点的编号
ans[ver]= min(ans[ver], d);
d+=line[i][j].y;//d每次加上到下一个点的距离
}
}
//输出结果
for(int i=2; i<=n; i++)
if(ans[i] == INF) puts("inf");
else printf("%lld\n", ans[i]);
return 0;
}