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\)。转移决策,我们考虑从哪个儿子走过来。

\[f_u=\min_{v\in son_u}\{f_v,c_u-\sum_{w\in T_v} b_w\} \]

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;
}
posted @ 2021-04-10 21:47  LarsWerner  阅读(52)  评论(0编辑  收藏  举报