这题很有意思,综合性也比较强。
我们发现,两个人都只会走最短路径,于是我们可以把参与点对最短路径的边标记出来(其他的边都没有影响)。显然,每个点对中因为只有参与最短路径的边,它们构成有向无环图。并且,最优答案中公共路径必然是一个连通块。这是因为,假设有两个分离的公共联通块,两条最短路径对它们的连接方式是一样的(两端相同,中间都走最短路),肯定可以走在一起,把两块连起来。我们挑出一对点重构后的图,在上面可以判断公共边,然后拓扑+dp就可以了。
首先,分别处理出两个点对的重构图。这一点可以从起点跑完最短路后从终点倒着找,通过判断边的起点的 值加上边权是否等于终点的
值 来确定此边是否可以在最短路径上,由于点数不多,可以使用两层邻接矩阵存两个点对重构的边。
随后,我们在其中一张图上按照拓扑排序dp,dp设有两个维度,假设当前在第点,
是它的前驱:
表示第
点所在的连续公共路径长度(并不要求当前最长);
表示起点起至第
点的最长连续公共路径长度(相当于答案);
当一条边是公共边时,=
,其中
是该边边权,表示第
点的值由第
点延伸;
=
,表示保持/用前驱的答案/用自己刚才更新的答案。
当一条边不是公共边时,,第
点不能参与当前公共路径;
=
,表示保持/用前驱的答案。
终点时的就是最后的结果了,但是要注意,两人相向而行的边也算公共边,这点不太好存,需要把另一张图方向反一反再来一遍。(显然路径中不会同时出现两人相向同向)
#include<bits/stdc++.h>
#define N 1509
#define M 600009
#define INF 0x3f3f3f3f
#define mod 998244353
using namespace std;
typedef long long ll;
typedef long double ldb;
typedef pair<int,int> pii;
int n,m,num,sum,en,xp,yp,xpp,ypp,ans;
int vis[N],head[M],to[M],val[M],nxt[M],dis[N],mp[2][N][N],flg[N][N],in[N],dp[N][2],flgg[2][N];
void add(int u,int v,int w){
to[++num]=v,nxt[num]=head[u],head[u]=num,val[num]=w;
}
void dij(int rt){
memset(vis,0,sizeof(vis));
memset(dis,INF,sizeof(dis));
priority_queue<pii,vector<pii>,greater<pii> > q;
dis[rt]=0;
q.push(make_pair(0,rt));
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u])continue;vis[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i],w=val[i];
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v])q.push(make_pair(dis[v],v));
}
}
}
}
void dfs(int x,int id){
if(flgg[id][x])return;
flgg[id][x]=1;
for(int i=head[x];i;i=nxt[i]){
int y=to[i],w=val[i];
if(dis[y]+w==dis[x]){
mp[id][y][x]+=w;//id表示边属于哪一条路径
dfs(y,id);
}
}
}
void topo(){
memset(in,0,sizeof(in));
memset(dp,0,sizeof(dp));
queue<int> q;q.push(xp);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mp[0][i][j])in[j]++;
}
}
while(!q.empty()){
int u=q.front();q.pop();
for(int v=1;v<=n;v++){
if(mp[0][u][v]){
if(mp[1][u][v]){//如果这是公共边
dp[v][0]=max(dp[v][0],dp[u][0]+mp[0][u][v]);//公共路径从前驱节点延伸
dp[v][1]=max(dp[v][1],max(dp[u][1],dp[v][0]));//更新答案,连不连这条边
}else{//不是公共边
dp[v][1]=max(dp[v][1],dp[u][1]);//从前驱节点继承
dp[v][0]=0;//a不能参与公共路径
}
in[v]--;
if(!in[v])q.push(v);
}
}
}
ans=max(ans,dp[yp][1]);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
cin>>xp>>yp>>xpp>>ypp;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dij(xp);dfs(yp,0);
dij(xpp);dfs(ypp,1);
topo();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mp[1][i][j]&&!(flg[i][j]||flg[j][i])){swap(mp[1][i][j],mp[1][j][i]);flg[i][j]++,flg[j][i]++;}//另一条路径反向
}
}
topo();
cout<<ans;
return 0;
}
文章讲述了如何通过Dijkstra算法找到两个人之间的最短路径,并利用重构图和拓扑排序求解公共路径,最终实现高效计算两人之间的公共边和最长公共路径长度。
1772

被折叠的 条评论
为什么被折叠?



