题解 十字路口

传送门

神仙题

核心切入点在于将题意转化为若干个变量在模周期 \(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;
}
posted @ 2022-03-26 19:42  Administrator-09  阅读(1)  评论(0编辑  收藏  举报