题解 十字路口
神仙题
核心切入点在于将题意转化为若干个变量在模周期 \(T\) 意义下同余
考虑这 \(m\) 次观察中,我们可以根据同一个灯的两个不都为 0 的剩余时间找到关于这两次观察的关系
第 \(i\) 次和第 \(j\) 次观察,示数为 \(x, y\)
\[i+x\equiv j+y\pmod T
\]
\[\therefore i-j\equiv y-x\pmod T
\]
那么可以从点 \(i\) 连一条向 \(j\) 的权为 \(y-x\) 的边
跑最小环可以得到周期 T
不理解是为什么 也不知道图里有多个连通块是这样为什么对(或者并不对)
这样复杂度 \(O(nm^2+m^3)\)
那么考虑弄一个复杂度瓶颈在于 \(n\) 的做法来根号分治
这个比较难想
考虑一个灯的变绿时间是固定的
那么在同一次观察中可以得到两个灯变化时间的关系
若观察灯 \(i, j\) 变化时间 \(t_i, t_j\),剩余时间 \(x, y\)
\[t_i-x\equiv t_j-y\pmod T
\]
\[\therefore t_i-t_j\equiv x-y\pmod T
\]
等量关系是因为两边都等于当前时间
同样建图 Floyd 即可
复杂度 \(O(nm\sqrt{nm})\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
vector<int> a[N];
int dis[325][325], ans=INF;
namespace task1{
void solve() {
for (int i=1; i<=m; ++i)
for (int k=1; k<=n; ++k) if (a[i][k])
for (int j=1; j<=m; ++j) if (a[j][k] && a[j][k]>a[i][k])
dis[i][j]=min(dis[i][j], a[j][k]-a[i][k]);
for (int i=1; i<=m; ++i) dis[i][i]=0;
for (int k=1; k<=m; ++k) for (int i=1; i<=m; ++i) for (int j=1; j<=m; ++j) dis[i][j]=min(dis[i][j], dis[i][k]+dis[k][j]);
for (int i=1; i<=m; ++i) for (int j=1; j<=m; ++j) if (i!=j) ans=min(ans, dis[i][j]+dis[j][i]);
cout<<(ans==INF?-1:ans)<<endl;
}
}
namespace task2{
void solve() {
for (int k=1; k<=m; ++k)
for (int i=1; i<=n; ++i) if (a[k][i])
for (int j=1; j<=n; ++j) if (a[k][j] && a[k][i]>a[k][j])
dis[i][j]=min(dis[i][j], a[k][i]-a[k][j]);
for (int i=1; i<=n; ++i) dis[i][i]=0;
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<=n; ++i) for (int j=1; j<=n; ++j) if (i!=j) ans=min(ans, dis[i][j]+dis[j][i]);
cout<<(ans==INF?-1:ans)<<endl;
}
}
signed main()
{
freopen("crossing.in", "r", stdin);
freopen("crossing.out", "w", stdout);
n=read(); m=read();
memset(dis, 0x3f, sizeof(dis));
for (int i=1; i<=m; ++i) a[i].resize(n+1);
for (int i=1; i<=m; ++i) for (int j=1; j<=n; ++j) a[i][j]=read();
if (m<=n) task1::solve();
else task2::solve();
return 0;
}