Kruskal 重构树
建树步骤:
- run Kruskal
- 对于连接两个不在一个连通块的点 (u,v),新建节点 (k),在重构树上连边 ((u,k), (v,k)),点权为 ((u,v)) 的边权。并查集中 ((u,v)) 都和 (k) 连。
性质:
-
叶子节点为图中节点,非叶子节点为新建的边节点。
-
(lca(u,v)) 代表 (u-v) 路径的瓶颈的最值(取决 Kruskal 的时候边怎样排序)。
-
是一个二叉堆结构(边权从根到叶是单调的)。
ARC098D Donation(不正经的 Kruskal 重构树)
考虑倒着来(捐钱 ( o) 收敛)。设 (c=max(a-b,0)),则约束条件变为到达一个点需要有 (c) 的钱,然后还能收敛 (b) 元钱。
将所有边按照 (c) 从小到大排序。由于这道题是点权,所以我们就并不需要新建点来表示边,直接把 (c) 更大的作为 (c) 更小的重构树父亲即可。
考虑 DP。(f_u) 表示从子树内某一点走到 (u) 所需的最少初始钱。显然叶节点的 (f_u=c_u)。对于答案,我们从某一点走到 (1) 然后即可遍历所有的节点,所以最终答案为 (f_1+sum b_u)。转移决策,我们考虑从哪个儿子走过来。
[f_u=min_{vin son_u}{f_v,c_u-sum_{win T_v} b_w}
]
https://www.luogu.com.cn/record/49245782
NOI2018 归程
考虑询问暴力怎么做。我们枚举切换节点 (u),满足 (v o u) 开车,(u o 1) 走路,这样的代价为 (dis_u),即 (1 o u) 的最短路。(v o u) 能开车,等价于 (v o u) 路径上的最小海拔要高于水位线。
这种瓶颈问题可以考虑用 Kruskal 重构树。按海拔 (a) 从大到小排序后,建出的 Kruskal 重构树满足祖先边的海拔一定 (<) 后代边的海拔。所以,我们对于每一个询问的 (v),找到深度最小的节点 (u) 满足子树内的点的海拔全部都 (>p),然后 (u) 子树中的节点都是可以乘车到达的。所以 (ans=min_{iin 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
",lst=ans[query(v,p)]);
}
}
return 0;
}