题目
思路
容易发现从 \(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;
}