这题开始我想复杂了,用Tarjan缩点然后在DAG上乱搞,后来冷静分析,题目中要我们求的其实就是1--n的路径中能得到的最大差价,令maxx[i]表示在1--n的路径中,i及i后面的点中水晶球价格的最大值,minn[i]表示在1--n的路径中,i及i前面的点中水晶球价格的最小值,这两个量是容易O(n)去维护的,但是问题是如何知道这个点在不在1--n的路径中。我们可以利用反图的思想,先以1为起点在正图中搜一遍,再以n为起点在反图中搜一遍,那么在正图中,凡是遍历到的点一定与1连通,在反图中,凡是遍历到的点一定与n连通,那么如果一个点被遍历了两次,那么它一定可以出现在1--n的路径中,如此问题就解决了。
现在反思上面的做法是错的,因为只是两次DFS并没有遍历所有的可能路径,结果因为这题数据水水过去了,对于minn[i]和maxx[i],我们要用SPFA不停的迭代更新,把下面程序中的DFS换成SPFA就没有问题了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1000005
int last[maxn][2],pre[maxn][2],other[maxn][2];
int w[maxn],minn[maxn],maxx[maxn],l,ans,n,m;
bool vis[maxn],flag1[maxn],flag2[maxn];
void connect(int x,int y)
{
l++;
pre[l][0]=last[x][0];
last[x][0]=l;
other[l][0]=y;
swap(x,y);
pre[l][1]=last[x][1];
last[x][1]=l;
other[l][1]=y;
}
void dfs1(int u)
{
vis[u]=1;flag1[u]=1;
for (int p=last[u][0];p;p=pre[p][0])
{
int v=other[p][0];
minn[v]=min(minn[u],minn[v]);
if (vis[v]) continue;
dfs1(v);
}
}
void dfs2(int u)
{
vis[u]=1;flag2[u]=1;
for (int p=last[u][1];p;p=pre[p][1])
{
int v=other[p][1];
maxx[v]=max(maxx[v],maxx[u]);
if (vis[v]) continue;
dfs2(v);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",w+i);
maxx[i]=minn[i]=w[i];
}
for (int i=1;i<=m;i++)
{
int a,b,opt;
scanf("%d%d%d",&a,&b,&opt);
connect(a,b);
if (opt==2) connect(b,a);
}
dfs1(1);
memset(vis,0,sizeof vis);
dfs2(n);
for (int i=1;i<=n;i++)
if (flag1[i]&&flag2[i]) ans=max(ans,maxx[i]-minn[i]);
printf("%d\n",ans);
return 0;
}