GMOJ5430. 图 题解
%%%高嘉煊巨佬为我们带来的质量极高的题目,和文采极佳的题解。
\(n\) 个点, \(m\) 条边的无向图,每条边有额外的信息 \(c\) , \(r\) ,有 \(q\) 组询问,每组询问四个参数 \(u\) , \(v\) , \(l\) , \(r\) 。表示一开始你在点 \(u\) 上,然后按顺序处理编号从 \(l\) 到 \(r\) 的边。对于第 \(i\) 条边,如果你当前在该边的端点上,那么你可以移动到另一个端点且代价为 \(c\) ,如不在该边端点或不移动则代价为 \(r\) 。询问到达 \(v\) 点的最小代价,如果不能到达,输出 -1 。
\(n\le30,m\le20000,q\le200000\)
思路
首先考虑 整体二分 。也就考虑了我整场比赛
对边二分,考虑已经处理出一个 \(mid\) 。
对于一个跨过 \(mid\) 的询问,我们 DP 出 \(fl_{i,j,k}(i\le mid)\) 表示从第 \(mid\) 条边到第 \(i\) 条边的从 \(j\) 出发到 \(k\) 的最小代价,以及 \(fr_{i,j,k}(i>mid)\) 表示从第 \(mid+1\) 条边到第 \(i\) 条边的从 \(j\) 出发到 \(k\) 的最小代价。
因为最短路是双向的所以并不需要考虑顺序正反。
然后枚举中间第 \(mid\) 条边结束后的点,合并一下两边的权值即可。
接下来对询问整体二分。
实现
首先就是对于 \(r\) 与 \(mid\) 重合的情况要特殊处理,以及一些其他的边界问题。
处理 \(fl\) 和 \(fr\) 数组时只用赋一层的初值,否则会 T 飞。
代码
含少量注释。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,k,e[20010][4],q[200010][5],le[200010][5],ri[200010][5],fl[20010][51][51],fr[20010][51][51],ans[200010];
char BuF[1<<26],*InF=BuF;
template<typename T>void read(T &x){
bool f=1;
for(;47>*InF||*InF>58;f^=*InF++=='-');
for(x=0;47<*InF&&*InF<58;x=(x<<3)+(x<<1)+(*InF++^48));
x=f?x:-x;
}
void divc(int l,int r,int ql,int qr){
if(ql>qr){
return;
}
register int mid=(l+r)>>1,cntl=0,cntr=0;
memset(fl[mid+1],63,sizeof(fl[mid+1])); //只赋一层初值
for(register int i=1;i<=n;++i){
fl[mid+1][i][i]=0;
}
for(register int i=mid;i>=l;--i){
for(register int j=1;j<=n;++j){
for(register int k=1;k<=n;++k){
fl[i][j][k]=fl[i+1][j][k]+e[i][3]; //直接赋值,确保INF可以正常转移
if(k==e[i][0]||k==e[i][1]){
fl[i][j][k]=min(fl[i][j][k],fl[i+1][j][e[i][k==e[i][0]]]+e[i][2]); //经过第i条边
}
}
}
}
memset(fr[mid],63,sizeof(fr[mid]));
for(register int i=1;i<=n;++i){
fr[mid][i][i]=0;
}
for(register int i=mid+1;i<=r;++i){
for(register int j=1;j<=n;++j){
for(register int k=1;k<=n;++k){
fr[i][j][k]=fr[i-1][j][k]+e[i][3];
if(k==e[i][0]||k==e[i][1]){
fr[i][j][k]=min(fr[i][j][k],fr[i-1][j][e[i][k==e[i][0]]]+e[i][2]);
}
}
}
}
for(register int i=ql;i<=qr;++i){ //我这里使用的没有等于的划分,因为可以等于也可以在该层计算无需递归
if(q[i][3]<mid){
memcpy(le[++cntl],q[i],sizeof(q[i])); //向左划分
}else if(q[i][2]>mid){
memcpy(ri[++cntr],q[i],sizeof(q[i])); //向右划分
}else if(q[i][3]>mid){ //对于右端点超出mid
for(int j=1;j<=n;++j){
ans[q[i][4]]=min(ans[q[i][4]],fl[q[i][2]][j][q[i][0]]+fr[q[i][3]][j][q[i][1]]);
}
}else{ //右端点与mid重合
ans[q[i][4]]=fl[q[i][2]][q[i][1]][q[i][0]];
}
}
for(register int i=1;i<=cntl;++i){
memcpy(q[ql+i-1],le[i],sizeof(le[i]));
}
for(register int i=1;i<=cntr;++i){
memcpy(q[qr-i+1],ri[i],sizeof(ri[i]));
}
if(l<r){
divc(l,mid,ql,ql+cntl-1);
divc(mid+1,r,qr-cntr+1,qr);
}
}
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
fread(BuF,1,1<<26,stdin);
read(n);read(m);read(k);
for(register int i=1;i<=m;++i){
read(e[i][0]);read(e[i][1]);read(e[i][2]);read(e[i][3]);
}
for(register int i=0;i<k;++i){
read(q[i][0]);read(q[i][1]);read(q[i][2]);read(q[i][3]);q[i][4]=i;
}
memset(ans,63,sizeof(ans));
divc(1,m,0,k-1);
for(register int i=0;i<k;++i){
printf("%d\n",ans[i]<0x3f3f3f3f?ans[i]:-1);
}
fclose(stdin);
fclose(stdout);
return(0);
}

浙公网安备 33010602011771号