[ICPC 46th Shanghai] Life is a Game 克鲁斯卡尔重构树

题目链接

题目大意:
给定n个点,m条边,有q个询问
每个点有一个(能量值)点权,每条边有一个边权
m条边描述为u v w表示有一条u与v相连的边权为w的通路

在每一次询问中,给定一个点x和现有的能量值k,每次只能是在当前能量值大于边权的时候到达另一个点,并获取这个点的能量值(路可以重复走),问最终能够获得多大的能量值

思路:
克鲁斯卡尔重构树
在建立克鲁斯卡尔重构树的时候,会将边权化为点权来处理并建立成一个堆,而且原先的节点一定是重构树中的叶子节点,除了叶子节点之外的其他点都是原先的边权
在克鲁斯卡尔重构树中,从叶子节点以上到根的路径中,点权一定是单调不减的

所以说给定的节点要是能够直接跳到祖先节点的话,那么说以这个节点为根的子树都能够访问到
所以说在这里可以使用倍增来处理,并且在维护以某节点为根的子树的所有节点的能量值
这样如果说能够跳过去的话,就将能量值的贡献加过去,直到不能跳为止

code:

ll fa[maxn],siz[maxn],a[maxn];
vector<int> gra[maxn];
ll bz[27][maxn],n,m,cnt,q;
struct node {
	int u,v,w;
	friend bool operator<(node a,node b) {
		return a.w < b.w;
	}
	node() {	}
	node(int _u,int _v,int _w) {
		u = _u,v = _v,w = _w;
	}
};
vector<node> vet;
int get(int x) {
	if(x == fa[x]) return x;
	else return fa[x] = get(fa[x]);
}
void init() {
	for(int i=0; i<=n*2; i++) fa[i] = i;//,siz[i] = 1;
}
void kru() {
	sort(vet.begin(),vet.end());
	for(int i=0; i<m; i++) {
		node nd = vet[i];
		int u = nd.u,v = nd.v,w = nd.w;
		int fau = get(u);
		int fav = get(v);
		if(fau != fav) {
			a[++cnt] = w;
			fa[cnt] = fa[fau] = fa[fav] = cnt;
			gra[fau].push_back(cnt);
			gra[cnt].push_back(fau);
			gra[fav].push_back(cnt);
			gra[cnt].push_back(fav);
		}
	}
}
void dfs(int u,int fa) {
	bz[0][u] = fa;
	for(int i=1; i<=20; i++) {
		bz[i][u] = bz[i-1][bz[i-1][u]];
	}
	for(int to:gra[u]) {
		if(to == fa) continue;
		dfs(to,u);
		siz[u] += siz[to];
	}
}
int main() {
	n = read,m = read,q = read;
	init();
	cnt = n;
	for(int i=1; i<=n; i++) siz[i] = read;
	for(int i=1; i<=m; i++) {
		int u = read,v = read,w = read;
		vet.push_back(node {u,v,w});
	}
	kru();
	dfs(cnt,0);
	a[0] = ((1LL<<31)-1);
	for(int i=1; i<=q; i++) {
		ll u = read,s = read;
		ll now = s + siz[u];
		while(u != cnt) {
			int tu = u;
			for(int i=20; i>=0; i--) {
				if(a[bz[i][u]] <= now) u = bz[i][u];
			}
			if(tu == u) break;
			now = siz[u] + s;
		}
// 		printf("->>>> %lld\n",now);
        printf("%lld\n",now);
	}
	return 0;
}
/**


**/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值