Kruskal 重构树
Kruskal 重构树
建树步骤:
- run Kruskal
- 对于连接两个不在一个连通块的点 \(u,v\),新建节点 \(k\),在重构树上连边 \((u,k), (v,k)\),点权为 \((u,v)\) 的边权。并查集中 \((u,v)\) 都和 \(k\) 连。
性质:
-
叶子节点为图中节点,非叶子节点为新建的边节点。
-
\(lca(u,v)\) 代表 \(u-v\) 路径的瓶颈的最值(取决 Kruskal 的时候边怎样排序)。
-
是一个二叉堆结构(边权从根到叶是单调的)。
ARC098D Donation(不正经的 Kruskal 重构树)
考虑倒着来(捐钱 \(\to\) 收敛)。设 \(c=\max(a-b,0)\),则约束条件变为到达一个点需要有 \(c\) 的钱,然后还能收敛 \(b\) 元钱。
将所有边按照 \(c\) 从小到大排序。由于这道题是点权,所以我们就并不需要新建点来表示边,直接把 \(c\) 更大的作为 \(c\) 更小的重构树父亲即可。
考虑 DP。\(f_u\) 表示从子树内某一点走到 \(u\) 所需的最少初始钱。显然叶节点的 \(f_u=c_u\)。对于答案,我们从某一点走到 \(1\) 然后即可遍历所有的节点,所以最终答案为 \(f_1+\sum b_u\)。转移决策,我们考虑从哪个儿子走过来。
https://www.luogu.com.cn/record/49245782
NOI2018 归程
考虑询问暴力怎么做。我们枚举切换节点 \(u\),满足 \(v\to u\) 开车,\(u\to 1\) 走路,这样的代价为 \(dis_u\),即 \(1\to u\) 的最短路。\(v\to u\) 能开车,等价于 \(v\to u\) 路径上的最小海拔要高于水位线。
这种瓶颈问题可以考虑用 Kruskal 重构树。按海拔 \(a\) 从大到小排序后,建出的 Kruskal 重构树满足祖先边的海拔一定 \(<\) 后代边的海拔。所以,我们对于每一个询问的 \(v\),找到深度最小的节点 \(u\) 满足子树内的点的海拔全部都 \(>p\),然后 \(u\) 子树中的节点都是可以乘车到达的。所以 \(ans=\min_{i\in T_u} d_{i}\)。
#include<bits/stdc++.h>
#define int long long
#define f(a) a.first
#define s(a) a.second
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=400009,inf=0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> pii;
inline long long read() {
register long long x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int T,n,m,cnt,val[N],d[N],f[N][29],ans[N],lst;
struct edge {int u,v,h;} ed[N];
bool cmp(const edge &a,const edge &b) {return a.h>b.h;}
vector<pii>e[N];
vector<int>t[N];
int id[N];
int find(int i) {return i==id[i]?i:id[i]=find(id[i]);}
void kruskal() {
sort(ed+1,ed+m+1,cmp);
rep(i,1,n) id[i]=i;
cnt=n;
rep(i,1,m) {
int u=ed[i].u,v=ed[i].v,h=ed[i].h;
if(find(u)!=find(v)) {
u=find(u), v=find(v);
val[++cnt]=h;
t[cnt].push_back(u), t[cnt].push_back(v);
id[u]=id[v]=id[cnt]=cnt;
}
}
rep(i,1,n) val[i]=inf;
}
bool vst[N];
void dijkstra() {
memset(d,0x3f,sizeof(d)), d[1]=0;
priority_queue<pii>q; q.push(pii(0,1));
while(!q.empty()) {
int u=s(q.top()); q.pop();
if(vst[u]) continue; vst[u]=1;
for(auto ev:e[u]) {
int v=f(ev), w=s(ev);
if(d[u]+w<d[v]) d[v]=d[u]+w, q.push(pii(-d[v],v));
}
}
}
void dfs(int u) {
ans[u]=d[u];
rep(h,1,20) f[u][h]=f[f[u][h-1]][h-1];
for(auto v:t[u]) {
f[v][0]=u;
dfs(v);
ans[u]=min(ans[u],ans[v]);
}
}
int query(int u,int p) {
per(h,20,0) if(f[u][h]&&val[f[u][h]]>p) u=f[u][h];
return u;
}
signed main() {
T=read();
while(T--) {
memset(ed,0,sizeof(ed)), memset(e,0,sizeof(e)), memset(t,0,sizeof(t));
memset(f,0,sizeof(f)), lst=0, cnt=0, memset(vst,0,sizeof(vst));
n=read(), m=read();
rep(i,1,m) {
int u=read(), v=read(), l=read(), a=read();
ed[i]=(edge){u,v,a};
e[u].push_back(pii(v,l)), e[v].push_back(pii(u,l));
}
kruskal();
dijkstra();
dfs(cnt);
int Q=read(), K=read(), S=read();
while(Q--) {
int v=read(), p=read();
v=(v+K*lst-1)%n+1, p=(p+K*lst)%(S+1);
printf("%lld\n",lst=ans[query(v,p)]);
}
}
return 0;
}