kruskal重构树,其实本质上就是个可持久化的kruskal。
kruskal算法是用来求解最小生成树的,而最小生成树有另外一个性质:它也是最小瓶颈树,即图上两点之间经过边权最大的边最小的路径,都是生成树上两点间的路径。我们利用这一性质,可以在kruskal算法的求解过程中处理一些东西,例如维护图上只保留边权小于某个值的边时,图的连通性。
但是,如果这一维护过程要求强制在线,那么就必须用到kruskal重构树了。
kruskal重构树是一棵有根二叉树,其实就是把kruskal的过程用一棵二叉树记录下来。每次用一条边合并两个连通点集时,新建一个节点,点权为加入边的边权,它的两个儿子分别为表示两个连通点集的节点。
根据kruskal算法,我们可以得到kruskal重构树的一些重要性质:
1. 如果忽略叶结点,kruskal重构树满足堆性质。
2. kruskal的每个子树是原图上保留边权不大于根节点权值的边后的极大连通子图。
根据kruskal算法我们可以很轻松的证明这些性质。
例题:
这道题需要在线维护无向图上从某个起点出发,经过边权不小于某个权值的边到达的点权最小的点。显然,从该点开始经过边权不超过给定权值的边所能到达的所有点,就是kruskal重构树上该起点的,点权不小于该权值的最远祖先的子树。所以我们把kruskal重构树建出来,然后对于每个节点倍增找祖先,然后直接查询子树内的最小点权即可。
因为实在找不到哪里爆int,所以就只能暴力#define int long long...
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<queue> #define ll long long #define int long long #define inf 1ll<<60 #define maxn 200010 inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0'; return x*f; } inline void write(ll x) { static char buf[20],len; len=0; if(x<0)putchar('-'),x=-x; for(;x;x/=10)buf[len++]=x%10+'0'; if(!len)putchar('0'); else while(len)putchar(buf[--len]); } inline void writesp(ll x){write(x); putchar(' ');} inline void writeln(ll x){write(x); putchar('\n');} struct Edge{ int x,y,h,d; }E[2*maxn]; struct edge{ int to,nxt,d; }e[4*maxn]; struct Data{ int id,x; friend bool operator < (Data a,Data b){return a.x>b.x;} }; std::priority_queue<Data>q; int fir[2*maxn],top[2*maxn],val[2*maxn],fa[2*maxn][20]; int mark[maxn],dist[2*maxn]; int n,m,Q,K,S,cnt,tot; bool cmp(Edge a,Edge b){return a.h>b.h;} int find(int x){return x==top[x]?x:top[x]=find(top[x]);} void add_edge(int x,int y,int d){e[tot].to=y; e[tot].d=d; e[tot].nxt=fir[x]; fir[x]=tot++;} void dijkstra(int S) { for(int i=1;i<=n;i++){ mark[i]=0; dist[i]=inf; } dist[S]=0; Data init={S,0}; q.push(init); while(!q.empty()){ Data now=q.top(); q.pop(); if(mark[now.id])continue; mark[now.id]=1; for(int i=fir[now.id];~i;i=e[i].nxt) if(dist[now.id]+e[i].d<dist[e[i].to]){ dist[e[i].to]=dist[now.id]+e[i].d; Data tmp={e[i].to,dist[e[i].to]}; q.push(tmp); } } } void dfs(int now) { for(int i=1;i<=18;i++) fa[now][i]=fa[fa[now][i-1]][i-1]; if(now>n)dist[now]=inf; for(int i=fir[now];~i;i=e[i].nxt){ fa[e[i].to][0]=now; dfs(e[i].to); dist[now]=std::min(dist[now],dist[e[i].to]); } } void work() { n=read(); m=read(); memset(fir,255,sizeof(fir)); tot=0; for(int i=1;i<=m;i++){ E[i].x=read(); E[i].y=read(); E[i].d=read(); E[i].h=read(); add_edge(E[i].x,E[i].y,E[i].d); add_edge(E[i].y,E[i].x,E[i].d); } dijkstra(1); std::sort(E+1,E+m+1,cmp); for(int i=1;i<=2*n;i++)top[i]=i; memset(fir,255,sizeof(fir)); tot=0; cnt=n; for(int i=1;i<=m;i++){ int fx=find(E[i].x),fy=find(E[i].y); if(fx!=fy){ val[++cnt]=E[i].h; add_edge(cnt,fx,0); add_edge(cnt,fy,0); top[fx]=top[fy]=cnt; } } fa[cnt][0]=0; dfs(cnt); Q=read(); K=read(); S=read(); int lastans=0; while(Q--){ int v=read(),p=read(); v=(v+K*lastans-1)%n+1; p=(p+K*lastans)%(S+1); // printf("%d %d *************\n",v,p); int now=v; for(int i=18;i>=0;i--) if(fa[now][i]&&val[fa[now][i]]>p)now=fa[now][i]; lastans=dist[now]; writeln(lastans); } // for(int i=1;i<=cnt;i++) // printf("%d %d %d %d\n",i,dist[i],fa[i][0],val[i]); } signed main() { int T=read(); while(T--)work(); return 0; }