Kruskal 重构树
基于 Kruskal 的优秀结构建出来的树。
模板
我们在跑 Kruskal 做最小生成树的时候,都是直接用并查集判断连通性。实际上我们可以利用这个过程建出来一个性质很强的东西。
每次我们插入一条边 \(u,v\) 的时候,我们都找到当前 \(u,v\) 在 Kruskal 重构树上的根节点,记为 \(u',v'\)。我们将 \(u',v'\) 两个点作为一个新建节点的儿子,这个新建节点的权值是 \(u,v\) 这条边的边权 \(w\)。然后我们跑完这个过程就建出了所谓的 Kruskal 重构树。
如果我们是跑最小生成树,那这棵树显然是一个大根堆,因为越晚插入的边权值越大,对应的新建节点的边权就越大。
这棵树的所谓强大性质是指跑最小生成树的时候,任意两个点之间的路径上点权的最大值(实际上就是 lca 的权值,毕竟是大根堆)就是这两个点间所有可能路径上的边的最大值的最小值。如果跑的是最大生成树则反之。
这个东西就可以用来维护一般无向图上的两点间路径的阈值问题,比如问你从一个点为起点有多少点对满足其所有可能的路径上最小值要大于某个数之类的。
P4768 [NOI2018] 归程
发现实际上就是找一些点,满足从给定的起点到这些点所有可能路径中边权最小值严格大于给定的 \(S\)。然后求这些点中到 1 的最短路的最小值。
考虑 Kruskal 重构树。显然是跑最大生成树。
发现这些点一定全部在 Kruskal 重构树上的某个点的子树中。我们对于 Kruskal 重构树上的每个非叶子节点维护其表示的边权以及倍增向上跳的祖先,类似于倍增求 lca 的预处理。
然后查询就是从给定的起点开始向上找到深度最小的且点权严格大于 \(S\) 的点,然后答案就是这个点的子树中到 1 的最短路的最小值。
直接 dfs 预处理即可做到。
code
没什么细节。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define pii pair <int,int>
#define mp make_pair
const int N=4e5+7,inf=1e9+7;
struct node{int u,v,h,w;}bian[N];
struct edge{int v,w;};
bool operator < (edge x,edge y){return x.w<y.w;}
bool cmp1(node x,node y){return x.h>y.h;}
int n,m,val[N],fa[N],rt,f[N][19],dep[N],dis[N],vis[N];
vector <int> q[N];vector <edge> p[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void init(){
for(int i=1;i<=m;i++)bian[i]={0,0,0,0};
for(int i=1;i<=n;i++)p[i].clear();
for(int i=1;i<2*n;i++){q[i].clear(),dis[i]=dep[i]=vis[i]=fa[i]=val[i]=0;for(int j=0;j<=18;j++)f[i][j]=0;}
}
void kru(){
for(int i=1;i<n*2;i++)fa[i]=i;
int cnt=n;sort(bian+1,bian+m+1,cmp1);
for(int i=1;i<=m;i++){
int u=bian[i].u,v=bian[i].v;u=find(u),v=find(v);
if(u==v)continue;
++cnt;q[u].push_back(cnt),q[cnt].push_back(u),q[v].push_back(cnt),q[cnt].push_back(v);val[cnt]=bian[i].h;
fa[u]=fa[v]=cnt;if(cnt==2*n-1)break;
}
}
void dij(){
priority_queue <pii> t;
t.push(mp(0,1));for(int i=2;i<2*n;i++)dis[i]=inf;dis[1]=0;
while(!t.empty()){
int u=t.top().second;t.pop();if(vis[u])continue;vis[u]=1;
for(edge tmp:p[u]){
int v=tmp.v,w=tmp.w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;t.push(mp(-dis[v],v));
}
}
}
}
void dfs1(int u,int fa){
f[u][0]=fa;dep[u]=dep[fa]+1;
for(int i=1;i<=18;i++)f[u][i]=f[f[u][i-1]][i-1];
for(int v:q[u]){
if(v==fa)continue;
dfs1(v,u);dis[u]=min(dis[u],dis[v]);
}
}
int query(int s,int h){
int u=s;
for(int i=18;i>=0;i--)if(val[f[u][i]]>h)u=f[u][i];
return dis[u];
}
void solve(){
cin>>n>>m;for(int i=1,u,v,w,h;i<=m;i++)cin>>u>>v>>w>>h,bian[i]={u,v,h,w},p[u].push_back({v,w}),p[v].push_back({u,w});
kru();rt=2*n-1;dij();dfs1(rt,0);val[0]=-inf;
int Q,K,S,lst=0;cin>>Q>>K>>S;
while(Q--){
int v,h;cin>>v>>h;v=(v+K*lst-1)%n+1,h=(h+K*lst)%(S+1);
lst=query(v,h);cout<<lst<<'\n';
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--)solve(),init();return 0;
}

浙公网安备 33010602011771号