NOI2018Day1T1 归程 并查集 kruskal kruskal重构树 倍增表 Dijkstra
原文链接https://www.cnblogs.com/zhouzhendong/p/NOI2018Day1T1.html
题目传送门 - 洛谷P4768
题意
给定一个无向连通图,有 $n$ 个点 $m$ 条边,每条边有两个属性:海拔$(a)$、距离$(l)$。
有 $Q$ 组询问,每组询问两个数 $v,p$,表示询问从点 $v$ 出发,从第一次走海拔高度不超过 $p$ 的边起算,问行走距离最小为多少。(即,在第一次走海拔高度不超过 $p$ 的边之前,走的所有边都是免费的)
$T$ 组数据,强制在线。
$1\leq T\leq 3,\ \ \ n\leq 2\times 10^5,\ \ \ m\leq 4\times 10^5,\ \ \ \ Q\leq 4\times 10^5,\ \ \ a,p\leq 10^9,\ \ \ l\leq 10^4,\ \ \ 1\leq v\leq n$
题解
洛谷老爷机貌似非常慢,比€€F老爷机慢。
我们先把问题转化一下。
预处理出点 $1$ 到每一个点的最短路长度 $dis$。
这个东西还好我用了堆优化的 Dijkstra 。后来听说: 关于 SPFA 它死了
每一次询问,就是:连接海拔高度大于 $p$ 的所有边,求 $v$ 能到达的点中的最小 $dis$ 值。
首先考虑离线做法。
我们按照海拔从高到低依次加边,用 kruskal 的做法生成森林。维护一下连通块的最小 $dis$ 值,然后顺便询问就可以了。
但是强制在线。
1. 可持久化并查集 $\Longrightarrow$ 可能会被卡常数。
2. Kruskal 重构树 + 倍增。
我们令合并时的新节点权值为当前海拔,然后预处理祖先倍增表。
每次询问,倍增到深度最小的海拔大于 $p$ 的节点,输出子树最小 $dis$ 值即可。
作为同步赛选手写出来了,但是本机测大样例最后一个点 $1.26s$ 。放到 UOJ 上面自定义测试一下 , $0.183s$ ……
事实证明€€F老爷机跑的还是挺快的,期望得分:100,实际得分:100。
代码
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <cmath> #include <vector> #include <queue> using namespace std; int read(){ char ch=getchar(); int x=0; while (!('0'<=ch&&ch<='9')) ch=getchar(); while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar(); return x; } const int N=200005,M=400005; int T,n,m; struct Gragh{ int cnt,y[M*2],z[M*2],nxt[M*2],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b,int c){ y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt; } }g; struct Edge{ int x,y,h; Edge(){} Edge(int _x,int _y,int _h){ x=_x,y=_y,h=_h; } friend bool operator < (Edge a,Edge b){ return a.h>b.h; } }e[M]; int dis[N],vis[N]; struct Node{ int x,d; Node(){} Node(int _x,int _d){ x=_x,d=_d; } friend bool operator < (Node x,Node y){ return x.d>y.d; } }; priority_queue <Node> Q; void Dijkstra(){ while (!Q.empty()) Q.pop(); for (int i=1;i<=n;i++) dis[i]=2e9+5; dis[1]=0; memset(vis,0,sizeof vis); Q.push(Node(1,0)); while (!Q.empty()){ Node now=Q.top(); Q.pop(); int x=now.x; if (vis[x]) continue; vis[x]=1,dis[x]=now.d; for (int i=g.fst[x];i;i=g.nxt[i]) Q.push(Node(g.y[i],dis[x]+g.z[i])); } } const int N2=N*2; int fa[N2],son[N2][2],h[N2],mindis[N2],Fa[N2][20]; int getf(int x){ return fa[x]==x?x:fa[x]=getf(fa[x]); } int Qu[N2],head,tail; int main(){ // freopen("return.in","r",stdin); // freopen("return.out","w",stdout); T=read(); while (T--){ n=read(),m=read(); g.clear(); for (int i=1;i<=m;i++){ int x=read(),y=read(),l=read(),a=read(); g.add(x,y,l); g.add(y,x,l); e[i]=Edge(x,y,a); } Dijkstra(); sort(e+1,e+m+1); memset(fa,0,sizeof fa); for (int i=1;i<=n*2;i++) fa[i]=i; for (int i=1;i<=n;i++) h[i]=e[1].h+1; int cnt=n; memset(son,0,sizeof son); memset(h,0,sizeof h); for (int i=1;i<=m;i++){ int x=getf(e[i].x),y=getf(e[i].y); if (x==y) continue; h[++cnt]=e[i].h; son[cnt][0]=x,son[cnt][1]=y; fa[x]=fa[y]=cnt; } head=tail=0; Qu[++tail]=cnt; while (head<tail){ int x=Qu[++head]; for (int i=1;i<19;i++) Fa[x][i]=Fa[Fa[x][i-1]][i-1]; for (int i=0;i<2;i++){ int y=son[x][i]; if (y){ Fa[y][0]=x; Qu[++tail]=y; } } } for (int i=tail;i>0;i--){ int x=Qu[i]; if (x<=n) mindis[x]=dis[x]; else mindis[x]=min(mindis[son[x][0]],mindis[son[x][1]]); } int q=read(),K=read(),S=read(); int lastans=0; h[0]=-1; while (q--){ int v=read(),p=read(); v=(v+K*lastans-1)%n+1; p=(p+K*lastans)%(S+1); for (int i=18;i>=0;i--) if (h[Fa[v][i]]>p) v=Fa[v][i]; printf("%d\n",lastans=mindis[v]); } } // fclose(stdin); // fclose(stdout); return 0; }