【SSLOJ】最短路

题目

思路

容易发现从 \(i\)\(j\) 的最优路径一定是先往右再往左。因为如果某一时刻往左走后再往右走,那么还不如在往左走的时刻直接往右走。
所以考虑如何求出 \(dis[i][j]\) 表示只往右走,\(i\)\(j\) 的最短路。
那么可以考虑枚举 \(j\),然后从 \(j-1\) 到 1 枚举 \(i\),容易发现走相同步数,走的越右显然更优,所以可以利用单调性求出 \(dis[i][j]\)
然后用类似 spfa 的想法,枚举 \(i\),将 \(dis[i][j]\) 从小到大排序,向前染色。每个点最多被染色一次。
时间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=6010;
int n,ans,a[N],b[N],dis[N][N],father[N];
bool used[N];
vector<int> pos[N];

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

int main()
{
	scanf("%d",&n);
	for (int i=2;i<=n;i++) scanf("%d",&b[i]);
	for (int i=2;i<=n;i++) scanf("%d",&a[i]);
	for (register int i=1;i<=n;i++)
	{
		for (register int j=i-1,k=i;j>=1;j--)
		{
			while (a[k]>j) k--;
			dis[j][i]=dis[k][i]+1;
		}
	}
	b[1]=1;
	for (register int i=1;i<=n;i++)
	{
		for (register int j=0;j<=n;j++)
		{
			pos[j].clear();
			used[j]=0; father[j]=j;
		}
		for (int j=i;j<=n;j++)
			pos[dis[i][j]].push_back(j);
		for (register int j=0;j<=n;j++)
			for (register int k=0;k<pos[j].size();k++)
			{
				int p=pos[j][k];
				if (used[p]) continue;
				used[p]=1;
				for (int q=find(p);q>=b[p];q=find(q))
				{
					if (dis[i][q]>dis[i][p]+1 || (dis[i][q]==0 && i!=q)) dis[i][q]=dis[i][p]+1;
					pos[dis[i][q]].push_back(q);
					father[q]=find(q-1);
				}
			}
	}
	for (register int i=1;i<=n;i++)
		for (register int j=1;j<=n;j++)
			ans^=(i+j)*dis[i][j];
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/13471736.html