zoukankan      html  css  js  c++  java
  • 【洛谷3547】[POI2013] CEN-Price List(BFS)

    题目链接

    • 有一张 (n) 个点 (m) 条边的无向图,边长均为 (a)。在原图中所有满足最短路长为 (2a) 的点对之间连一条边长为 (b) 的边。
    • 求给定点 (st) 到所有点的距离。
    • (1le n,mle10^5)(1le a,ble10^3)

    原图上的 BFS

    根据 (b)(a) 的大小关系,最短路径可能有三种:全是 (a);若干 (b) 和一个 (a);全是 (b)

    先在原图中 BFS 一遍,求出 (st) 到所有点的最小边数 (d_i)

    也就是说,对于全是 (a) 的情况,最短路径长度就是 (d_i imes a)

    根据最短路的性质,最短路上第 (k) 个点和第 (k+2) 个点之间必然无边(否则就不是最短路了),因此我们必然可以把这条最短路径压缩,但可能会多出一个 (a)

    也就是说,对于若干个 (b) 和一个 (a) 的情况,最短路径长度就是 (lfloorfrac{d_i}2 floor imes b+(d_ioperatorname{mod}2) imes a)

    只走 (b) 的BFS

    然后考虑全是 (b) 的情况,我们重新 BFS 一遍,要求每次只能走 (b)

    有点类似于求三元环,但我们要求的实际上是三元非环。先枚举一遍当前点 (x) 连向的点 (y) 打标记,再枚举一遍当前点 (x) 连向的点 (y) 并枚举 (y) 连向的点 (z),如果 (z) 没被打上标记,那么就可以从 (x)(z) 转移。

    但我们并没有进行求三元环预先给边定向的操作,并且也无法进行这个操作,因此这样的复杂度是不正确的。

    不过考虑 BFS 的性质,就是每个点 (z) 只会被转移一次。我们不妨在转移后就把这条边给删掉。

    注意,具体实现中需要维护两个边集,其中枚举 (x) 连向的点是用原边集,枚举 (y) 连向的点是用转移边集,而我们删边仅是在转移边集中删去。

    因此,第二重枚举中的一条边被枚举到时,除非它构成了一个三元环,否则就会被删去。而三元环的个数是 (O(msqrt m)) 的,故复杂度正确。

    代码:(O(msqrt m))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 100000
    #define INF (int)1e9
    using namespace std;
    int n,m,st,a,b,d[N+5],q[N+5],vis[N+5],ans[N+5];
    struct Graph
    {
    	int ee,lnk[N+5];struct edge {int to,pre,nxt;}e[2*N+5];I Graph() {ee=1;}
    	I int operator [] (CI x) Cn {return lnk[x];}I edge operator () (CI x) Cn {return e[x];}
    	I void Add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]].pre=ee,e[lnk[x]=ee].to=y;}
    	I void Del(CI id) {(e[id].pre?e[e[id].pre].nxt:lnk[e[id^1].to])=e[id].nxt,e[e[id].nxt].pre=e[id].pre;}//删边
    }G,K;
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    int main()
    {
    	RI i,j,x,y;for(read(n,m,st,a,b),i=1;i<=m;++i) read(x,y),G.Add(x,y),G.Add(y,x),K.Add(x,y),K.Add(y,x);
    	RI k,H,T;for(i=1;i<=n;++i) d[i]=-1;d[q[H=T=1]=st]=0;
    	W(H<=T) for(i=G[k=q[H++]];i;i=G(i).nxt) !~d[G(i).to]&&(d[q[++T]=G(i).to]=d[k]+1);//原图上的BFS
    	for(i=1;i<=n;++i) ans[i]=~d[i]?min(d[i]*a,(d[i]&1)*a+(d[i]>>1)*b):INF;
    	for(i=1;i<=n;++i) d[i]=-1;d[q[H=T=1]=st]=0;W(H<=T) {for(i=G[k=q[H++]];i;i=G(i).nxt) vis[G(i).to]=k;//给当前点连向的点打标记
    		for(i=G[k];i;i=G(i).nxt) for(j=K[G(i).to];j;j=K(j).nxt) vis[K(j).to]^k&&(K.Del(j),!~d[K(j).to]&&(d[q[++T]=K(j).to]=d[k]+1));}//二重枚举,不构成三元环即可删边
    	for(i=1;i<=n;++i) ~d[i]&&(ans[i]=min(ans[i],d[i]*b));for(i=1;i<=n;++i) writeln(ans[i]);return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    手机端html滑动处理
    css控制div上下移动
    倒计时javascript
    PHP解决抢购等阻塞式高并发redis处理思路
    jQuery判断当前元素是第几个元素
    CSS 实现盒子水平居中、垂直居中和水平垂直居中的方法
    yii1.* session无法调用问题
    百度小程序坑坑坑
    php等比缩放图片
    lavarel的小失误
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3547.html
Copyright © 2011-2022 走看看