「SDOI 2009」Elaxia的路线
发现自己这几天智商完全不在线……
这道题的数据十分的水,怎样都可以艹过去……
开始想了一个完全错误的算法,枚举一对点,判断这一对点是否同时在两条最短路上,是就用两点之间的路径更新答案。显然这样是错的:
8 10
7 8 2 5
1 3 1
3 5 2
3 6 2
3 8 4
5 8 4
6 8 2
5 6 4
6 2 9
7 5 7
6 7 9
答案显然是 0,但是这个算法会输出 3。
但是实际上这个做法是可以 AC 的,但是我只有 30 分,原因先按下不表。
然后点开了讨论,发现我的做法果然是错的,然后发现正解是枚举一条边然后判断是否在公共最短路上,然后再在这些边中找最长链。
好的,没问题,差不太多,改一下就行。
改完交一发,还是 WA。
再看讨论区的做法明明和我一样,凭什么不让老子过?
然后我又发现这个做法也被 hack 了,就在我刚看到的这个做法的地方下面一点点。
艹,我刚才为什么不把讨论区看完?
因为我判断一条边是否在最短路上还是判断这条边链接两点是否在最短路上,这样依然是错的:
4 5
1 2 3 4
1 2 2
1 3 2
1 4 2
2 4 3
2 3 1
可以发现点1,2在公共最短路上,但是边1-2却不在公共最短路上。
我天真的以为是数据加强了,于是去找题解。结果发现百度第一篇题解 TM 就是上面的错误做法?终于,我找到了 PoPoQQQ 的题解,他的做法是没有问题的。
正确的做法是判断 \(dis[s][u]+w+dis[v][t] = dis[s][t]\),因为样例两人实际上是反向走的,所以要分别考虑 \(s1\) -> \(t1\) 和 \(s2\) -> \(t2\) 最长的重合部分与 \(s1\) -> \(t1\) 和 \(t2\) -> \(s2\)
的最长的重合部分。
注意不能一次计算,下面给一组讨论区 hack 数据:
8 9
1 6 7 8
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
7 3 5
7 5 5
2 8 4
4 8 4
然后,我重写了一遍,一遍过掉了样例,信心满满的交了一发,TM 还是 30 分。
接着我上面那组数据把自己 hack 掉了。
不对,我测过 PoPoQQQ 的代码,它可以过这组数据啊,那么一定是我细节写错了……
震惊!【SDOI 2009】Elaxia的路线始终 30 分的原因竟是——
dijkstra 求最短路的时候这样子写:
priority_queue <int> q;
以点的编号而不是到源点的距离为关键字……
这样居然还能过样例……
服气,无 fuck 说……
而上面的三种做法(没错包括第一种)只要我把 dijkstra 写对就能 AC。
吃枣药丸。
#include <bits/stdc++.h>
using namespace std;
#define RG register
#define N 2000
#define M 2500000
#define inf 536870912
inline int gi()
{
RG int ret; RG char ch;
ret=0, ch=getchar();
while (ch < '0' || ch > '9')
ch=getchar();
while (ch >= '0' && ch <= '9')
ret=(ret<<1)+(ret<<3)+ch-'0', ch=getchar();
return ret;
}
struct state
{
int o,dis;
inline bool operator <(const state S) const { return dis > S.dis; }
};
queue <int> Q;
priority_queue <state> q;
int n,m,num,cnt,ans;
int f[N],dis[5][N],et[M],ed[M],nx[M],id[5],topo[N],du[N],h[N];
bool vis[N];
struct edge
{
int to,nx,dis;
}e[M];
inline void add(int x,int y,int z)
{
et[++num]=y, ed[num]=z, nx[num]=f[x], f[x]=num;
}
inline void link(int x,int y,int z)
{
e[++cnt]=(edge){y,h[x],z}, h[x]=cnt;
}
inline void dijkstra(int u)
{
state st;
int i,o,to,len,Dis;
for (i=1; i<=n; ++i)
vis[i]=false, dis[u][i]=inf;
o=id[u];
dis[u][o]=0, q.push((state){o,0});
while (!q.empty())
{
st=q.top(), q.pop();
o=st.o, Dis=st.dis;
if (vis[o])
continue;
vis[o]=true;
for (i=f[o]; i; i=nx[i])
{
to=et[i], len=ed[i];
if (dis[u][to] > Dis+len)
dis[u][to]=Dis+len, q.push((state){to,dis[u][to]});
}
}
}
inline void topology()
{
int i,o,to,len;
for (i=1; i<=n; ++i)
if (!du[i])
Q.push(i);
while (!Q.empty())
{
o=Q.front(), Q.pop();
ans=max(ans,topo[o]);
for (i=h[o]; i; i=e[i].nx)
{
to=e[i].to, len=e[i].dis;
topo[to]=max(topo[to],topo[o]+len);
if (!(--du[to]))
Q.push(to);
}
}
}
int main()
{
// freopen("way.in","r",stdin);
// freopen("way.out","w",stdout);
int i,x,y,z,l1,l2;
n=gi(), m=gi();
for (i=1; i<5; ++i)
id[i]=gi();
num=1;
for (i=1; i<=m; ++i)
x=gi(), y=gi(), z=gi(), add(x,y,z), add(y,x,z);
for (i=1; i<5; ++i)
dijkstra(i);
l1=dis[1][id[2]], l2=dis[3][id[4]];
for (x=1; x<=n; ++x)
for (i=f[x]; i; i=nx[i])
{
y=et[i], z=ed[i];
if (dis[1][x]+z+dis[2][y] != l1 || dis[3][x]+z+dis[4][y] != l2)
continue;
link(x,y,z), du[y]++;
}
topology();
for (i=1; i<=n; ++i)
h[i]=topo[i]=0;
for (x=1; x<=n; ++x)
for (i=f[x]; i; i=nx[i])
{
y=et[i], z=ed[i];
if (dis[1][x]+z+dis[2][y] != l1 || dis[3][y]+z+dis[4][x] != l2)
continue;
link(x,y,z), du[y]++;
}
topology();
printf("%d\n",ans);
return 0;
}