• [CF843D]Dynamic Shortest Path


    [CF843D]Dynamic Shortest Path

    题目大意:

    给定一个带权有向图,包含(n(nle10^5))个点和(m(mle10^5))条边。共(q(qle2000))次操作,操作包含以下两种:

    • (1:v)——查询从(1)(v)的最短路。
    • (2:c:l_1:l_2:ldots:l_c)——将边(l_1,l_2,ldots,l_c)增加(1)的权值。

    思路:

    首先使用Dijkstra算法求出原图的单源最短路径(dis[i])。对于所有的操作(2),考虑增加边权后对答案的影响。不难发现每次修改边权后(dis[i])都会增加一定量或保持不变。不妨将每次每个点的增加量记作(add[i]),考虑增加边权后计算(add[i])的值。

    类比Dijkstra算法的“松弛”操作,对于一个结点(x),若(add[x] e0),我们可以用(x)来松弛别的结点。枚举(x)的下一个结点(y),若此时用(x)作为最短路中的上一任结点,则最短路长度需要增加(dis[x]+w(x,y)+add[x]-dis[y])。而(add[y])则需要对所有这样的值取(min)。这样完成所有的松弛操作后,(dis'[i]=dis[i]+add[i])。而这可以用BFS实现,其中当(add[i]>c)时则没有“松弛”的必要,可以进行剪枝。

    配对堆优化Dijkstra复杂度(mathcal O(nlog n+m)),单次BFS更新最短路(mathcal O(q(n+m))),总时间复杂度(mathcal O(nlog n+m+q(n+m)))

    细节:

    注意边权可能为(0),因此Dijkstra中被松弛的结点可能会跑到堆顶,不能松弛完再删除堆顶元素。本题时间限制较紧,实现时注意优化常数。

    源代码:

    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<climits>
    #include<algorithm>
    #include<functional>
    #include<forward_list>
    #include<ext/pb_ds/priority_queue.hpp>
    using int64=long long;
    inline int getint() {
    	register char ch;
    	while(!isdigit(ch=getchar()));
    	register int x=ch^'0';
    	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    	return x;
    }
    constexpr int N=1e5+1;
    int n,w[N],add[N];
    int64 dis[N];
    using Edge=std::pair<int,int>;
    std::forward_list<Edge> e[N];
    using Vertex=std::pair<int64,int>;
    __gnu_pbds::priority_queue<Vertex,std::greater<Vertex>> q;
    __gnu_pbds::priority_queue<Vertex,std::greater<Vertex>>::point_iterator p[N];
    inline void dijkstra() {
    	for(register int i=1;i<=n;i++) {
    		p[i]=q.push({dis[i]=i==1?0:LLONG_MAX,i});
    	}
    	while(!q.empty()&&q.top().first!=LLONG_MAX) {
    		const int x=q.top().second;
    		q.pop();
    		for(register auto &j:e[x]) {
    			const int &y=j.first,&w=::w[j.second];
    			if(dis[x]+w<dis[y]) {
    				q.modify(p[y],{dis[y]=dis[x]+w,y});
    			}
    		}
    	}
    	q.clear();
    }
    std::queue<int> v[N];
    int main() {
    	n=getint();
    	const int m=getint(),q=getint();
    	for(register int i=1;i<=m;i++) {
    		const int u=getint(),v=getint();
    		w[i]=getint();
    		e[u].emplace_front(std::make_pair(v,i));
    	}
    	dijkstra();
    	for(register int i=1;i<=n;i++) {
    		if(dis[i]==LLONG_MAX) dis[i]=-1;
    	}
    	for(register int i=0;i<q;i++) {
    		if(getint()==1) {
    			printf("%lld
    ",dis[getint()]);
    		} else {
    			const int c=getint();
    			for(register int i=0;i<c;i++) w[getint()]++;
    			std::fill(&add[1],&add[n]+1,c+1);
    			v[add[1]=0].emplace(1);
    			for(register int i=0;i<=c;i++) {
    				for(;!v[i].empty();v[i].pop()) {
    					const int &x=v[i].front();
    					if(add[x]!=i) continue;
    					for(register auto &j:e[x]) {
    						const int &y=j.first,&w=::w[j.second];
    						const int64 d=dis[x]+w+add[x]-dis[y];
    						if(d<add[y]) v[add[y]=d].emplace(y);
    					}
    				}
    			}
    			for(register int i=1;i<=n;i++) {
    				if(add[i]!=c+1) dis[i]+=add[i];
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    linux基础(六) --- 杀死包含应用名称的所有进程
    linux基础(五) ---ubuntu 修改国内源
    linux基础----vim编辑器(三)
    linux基础----vim编辑器(二)
    linux基础----vim编辑器(一)
    计算机基础----必会单词
    支付宝还可以更进一步的改造世界。
    insertAdajcentHTML
    11个触摸设备的触摸时间处理
    手机上的jQuery
  • 原文地址:https://www.cnblogs.com/skylee03/p/9118651.html
Copyright © 2020-2023  润新知