P4814 题解
看到这题还没题解,于是想着和大佬搞一下。
暴力
先考虑暴力,可以用各种奇怪的算法跑出起点
但是因为图是有环的,并且允许重复经过点和边,所以我一开始想用 K 短路来写,从小到大把所有路径跑出来,等路径长度大于
总而言之,时间复杂度爆炸。
正解思路(?)
放弃暴力地跑出路径,而是考虑每条边是否会被打上
不妨设
则很容易想到,对于长度为
所以我用 dijkstra 跑正反图,预处理出所有
code
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
int n,m,s,h1[5001000],dis1[5001000],l1,vis[5001000],x,y,z,e,w,dis2[5001000],h2[5001000],l2,qq,ans[5001000],zjp=1;
struct node
{
int dis,pos;
bool operator <(const node &x)const
{
return x.dis<dis;
}
};
struct abc
{
int pos,val;
}D[5001000];
struct ljh
{
int dis,cost;
}znx[5001000];
inline int read()
{
int k=0,f=0;char c=getchar();
for(;!isdigit(c);c=getchar()) f|=c=='-';
for(;isdigit(c);c=getchar()) k=(k<<1)+(k<<3)+(c^48);
return f?-k:k;
}
priority_queue<node> q;
struct edge
{
int v,next,w,cost,u;
}e1[500100],e2[500100];
inline void add1(int x,int y,int z,int w)
{
e1[++l1].v=y;
e1[l1].u=x;
e1[l1].w=z;
e1[l1].cost=w;
e1[l1].next=h1[x];
h1[x]=l1;
}
inline void add2(int x,int y,int z,int w)
{
e2[++l2].v=y;
e2[l2].w=z;
e2[l2].u=x;
e2[l2].cost=w;
e2[l2].next=h2[x];
h2[x]=l2;
}
inline void d1()//模板
{
for(int i=1;i<=n;i++) dis1[i]=inf;
dis1[s]=0;
q.push((node){0,s});
while(!q.empty())
{
node t=q.top();
q.pop();
int k=t.pos;
if(vis[k]) continue;
vis[k]=1;
for(int i=h1[k];i;i=e1[i].next)
{
if(dis1[e1[i].v]>dis1[k]+e1[i].w)
{
dis1[e1[i].v]=dis1[k]+e1[i].w;
if(!vis[e1[i].v]) q.push((node){dis1[e1[i].v],e1[i].v});
}
}
}
}
inline void d2()//模板
{
for(int i=1;i<=n;i++)
{
dis2[i]=inf;
vis[i]=0;
}
dis2[e]=0;
q.push((node){0,e});
while(!q.empty())
{
node t=q.top();
q.pop();
int k=t.pos;
if(vis[k]) continue;
vis[k]=1;
for(int i=h2[k];i;i=e2[i].next)
{
if(dis2[e2[i].v]>dis2[k]+e2[i].w)
{
dis2[e2[i].v]=dis2[k]+e2[i].w;
if(!vis[e2[i].v]) q.push((node){dis2[e2[i].v],e2[i].v});
}
}
}
}
inline bool cmp1(abc x,abc y){return x.val<y.val;}
inline bool cmp2(ljh x,ljh y){return x.dis<y.dis;}
signed main()
{
n=read(),m=read(),s=read(),e=read();
for(int i=1;i<=m;i++)
{
x=read(),y=read(),z=read(),w=read();
add1(x,y,z,w);
add2(y,x,z,w);
}
qq=read();
for(int i=1;i<=qq;i++)
{
D[i].val=read();
D[i].pos=i;
}
sort(D+1,D+qq+1,cmp1);
d1();//正图
d2();//反图
for(int i=1;i<=m;i++)
{
znx[i].dis=dis1[e1[i].u]+e1[i].w+dis2[e1[i].v];
znx[i].cost=e1[i].cost;
}
sort(znx+1,znx+m+1,cmp2);//询问离线
for(int i=1;i<=qq;i++)
{
ans[D[i].pos]=ans[D[i-1].pos];//先继承一定会打上 tag 的边
while(znx[zjp].dis<=D[i].val&&zjp<=m)//如果可以加入新的边
{
ans[D[i].pos]+=znx[zjp].cost;
zjp++;
}
}
for(int i=1;i<=qq;i++) printf("%lld\n",ans[i]);
return 0;
}