CF843D Dynamic Shortest Path
带权有向图,支持一下操作:
- 询问\(1\)到某个点\(v\)的最短路。
- 读入一个边集,将这些边的边权都加\(1\)。
\(n,m\le 10^5,Q\le 2000\)
正解可以猜到是\(O(mQ)\)级别的。
如果最大距离为\(W\),可以用\(O(w)\)大小的桶来优化dijkstra,做到\(O(m+W)\)的时间。
注意到每次边权的变化量都很小,于是\(\Delta w\le n-1\)。
用改边权的套路来把\(W\)缩小:先跑一边普通的最短路求出\(dis_i\),然后将边\((u,v,w)\)的\(w\)改成\(dis_u+w-dis_v\)。由三角形不等式得修改后边权仍然非负。
现在对边权进行修改,然后再跑最短路,这时候跑出来的\(dis'_i\)表示\(dis_i\)的增加量。
由于一定有\(dis_i'\le n-1\),所以每次时间是\(O(m+n)\)。
跑完之后继续更新边权即可。
using namespace std;
#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define INF 100000000000000
int n,m;
struct EDGE{
int to,w;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
void link(int u,int v,int w){
e[ne]={v,w,last[u]};
last[u]=e+ne++;
}
ll dis[N],f[N];
void adjust(){
for (int i=1;i<=n;++i)
for (EDGE *ei=last[i];ei;ei=ei->las)
ei->w+=dis[i]-dis[ei->to];
for (int i=1;i<=n;++i)
f[i]+=dis[i];
}
void SP0(){
typedef pair<ll,int> info;
static priority_queue<info,vector<info>,greater<info> > q;
memset(dis,127,sizeof(ll)*(n+1));
dis[1]=0;
q.push(mp(0,1));
while (!q.empty()){
int x=q.top().se;
ll s=q.top().fi;
q.pop();
if (s!=dis[x])
continue;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (s+ei->w<dis[ei->to]){
dis[ei->to]=s+ei->w;
q.push(mp(dis[ei->to],ei->to));
}
}
adjust();
}
void SP1(){
static vector<int> b[N];
memset(dis,127,sizeof(ll)*(n+1));
dis[1]=0;
b[0].push_back(1);
for (int i=0;i<n;++i){
for (int j=0;j<b[i].size();++j){
int x=b[i][j];
if (dis[x]<i)
continue;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (i+ei->w<n && i+ei->w<dis[ei->to]){
dis[ei->to]=i+ei->w;
b[dis[ei->to]].push_back(ei->to);
}
}
b[i].clear();
}
adjust();
}
int main(){
// freopen("in.txt","r",stdin);
int Q;
scanf("%d%d%d",&n,&m,&Q);
for (int i=0;i<m;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
link(u,v,w);
}
SP0();
while (Q--){
int op;
scanf("%d",&op);
if (op==1){
int v;
scanf("%d",&v);
printf("%lld\n",dis[v]>INF?-1:f[v]);
}
else{
int c;
scanf("%d",&c);
for (int i=0;i<c;++i){
int id;
scanf("%d",&id),--id;
e[id].w++;
}
SP1();
}
}
return 0;
}