1. 程式人生 > 實用技巧 >【SSLOJ】最短路

【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;
}