RMQ ZOJ 3649 Social Net

本文介绍如何使用Kruskal算法构建最大生成树,并通过建立RMQ(Range Maximum Query)来解决特定路径上的最大差值问题。文章详细解释了实现过程及核心代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Social Net

 


 

Time Limit: 5 Seconds      Memory Limit: 65536 KB

 


 

There are n individuals(2 <= n <= 30000). Everyone has one or more friends. And everyone can contact all people by friend-relation. If two persons aren't friends, they also can contact by their friends. Each pair of friends have a friendship value ai(1 <= ai <= 50000).

Firstly, you will relieve some friend-relation. The rest of the friend-relation is the social net. The net is unique in all test cases. In this net, everyone can contact all people by rest friend-relation. The net has a minimum number of friend-relation. And the net has maximum sum of friendship value. We want to get the maximum sum.

Secondly, everyone has an angry value bi(1 <= bi <= 100000). We have q operations(1 <= q <= 30000): Person X wants to contact person Y, this operation merely has one sequence which describes the process. The sequence consists of persons' angry value. The persons are on the process.

We suppose the sequence is c1c2c3, ... ,ci. Here ci means the angry value of the ith people in the sequence.

We attempt to find the maximum ck-cj (ck >= cjj <= k).

Example:

The sequence is 3(X), 4, 5, 6, 7, 5, 9, 4, 11(Y). The maximum ck-cj is 11-3=8.

The sequence is 3(X), 4, 5, 6, 7, 5, 9, 2, 11(Y). The maximum ck-cj is 11-2=9.

The sequence is 3(X), 10, 2, 5(Y). The maximum ck-cj is 10-3=7.

Input

The input contains multiple test cases. Each test case begins with a line containing a single integer n. The following line contains n integers bi.

The subsequent line describe the number of relations m(n <= m <= 50000). The next m lines contain the information about relations: xyai. Their friendship value is ai.

Afterward gives q. The next q lines contain the operations: xy. person X wants to contact person Y.

Output

For each case, print maximum sum of friendship value of the net on the first line.

The next q lines contain the answers of every operations.

Sample Input
6
3 5 1 7 3 5
7
1 2 5
1 3 6
2 4 7
2 5 8
3 6 9
4 5 1
5 6 2
5
6 1
6 2
6 3
6 4
6 5
Sample Output
35
2
4
0
6
4

 

 

题意:首先构建一个最大生成树,然后会有若干个询问(a,b),问从a到b中最大的ci-cj 。

 

思路:我们用Kruskal 构建一个最大生成树,然后建好树后,就建立rmq,其中需要知道某一段的最大值最小值,要知道某一段的最大的差值(就是询问那个值),其中一个是父亲减子孙的,还要一个是子孙减父亲的。询问的时候就不多说了看代码吧。

 

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string.h>
using namespace std;
const int maxn = 30000 + 5;
#pragma comment(linker,"/STACK:1024000000,1024000000")
int n, m, Q;
int maxv[maxn][16];
int minv[maxn][16];
int maxdiff[maxn][16];
int diff_reverse[maxn][16];
int anc[maxn][16];
int L[maxn];
int c[maxn], p[maxn];
int find(int x)
{
	if (x == p[x]) return x;
	return p[x] = find(p[x]);
}

inline int max(int a, int b) { return a > b ? a : b; }
inline int min(int a, int b) { return a < b ? a : b; }

struct Edge
{
	int u, v, w;
	bool operator<(const Edge&e)const
	{
		return w >e.w;
	}
}edges[50000+5];

struct Node
{
	int v;
	Node*next;
}*first[maxn],adj[maxn*2];
int ptr;
void add(int u, int v)
{
	adj[++ptr].v = v;
	adj[ptr].next = first[u];
	first[u] = &adj[ptr];
}

void input()
{
	for (int i = 1; i <= n; ++i) scanf("%d", c + i);
	scanf("%d", &m);
	for (int i = 0; i < m; ++i) scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].w);
	sort(edges, edges + m);
	for (int i = 1; i <= n; ++i) p[i] = i;
	int rest = n-1;
	int sum = 0;
	memset(first, 0, sizeof(first)); ptr = 0;
	for (int i = 0; i < m && rest; ++i) {
		int u = find(edges[i].u), v = find(edges[i].v);
		if (u == v) continue;
		--rest;
		p[u] = v;
		sum += edges[i].w;
		add(edges[i].u, edges[i].v);
		add(edges[i].v, edges[i].u);
	}
	printf("%d\n", sum);
}

void buildtree(int u,int fa)
{
	anc[u][0] = fa;
	maxv[u][0] = minv[u][0] = c[u];
	for (Node*p = first[u]; p; p = p->next){
		int v = p->v;
		if (v == fa) continue;
		L[v] = L[u] + 1;
		buildtree(v, u);
	}
}

void rmq()
{
	for (int j = 1; j < 16; ++j) {
		for (int i = 1; i <= n; ++i) {
			int a = anc[i][j - 1];
			if (a == -1) continue;
			anc[i][j] = anc[a][j - 1];
			maxv[i][j] = max(maxv[a][j - 1], maxv[i][j - 1]);
			minv[i][j] = min(minv[i][j - 1], minv[a][j - 1]);
			maxdiff[i][j] = max(maxdiff[i][j - 1], maxdiff[a][j - 1]);
			maxdiff[i][j] = max(maxdiff[i][j], maxv[a][j - 1] - minv[i][j - 1]);
			diff_reverse[i][j] = max(diff_reverse[i][j - 1], max(diff_reverse[a][j - 1], maxv[i][j - 1] - minv[a][j - 1]));
		}
	}
}

int query(int a, int b)
{
	if (a == b) return 0;
	int maxL = max(L[a], L[b]);
	int ret = -1;
	int l_min = c[a], r_max = c[b];
	int k;
	for (k = 0; (1 << k) <= maxL; ++k); --k;
	if (L[a] > L[b]) {
		for (int j = k; j >= 0;--j) 
		if (L[a] - (1 << j) >= L[b]) {
			ret = max(ret, max(maxv[a][j]-l_min,maxdiff[a][j]));
			l_min = min(l_min,minv[a][j]);
			a = anc[a][j];
		}
	}
	else if (L[a] < L[b]) {
		for (int j = k; j >= 0;--j) 
		if (L[b] - (1 << j) >= L[a]) {
			ret = max(ret, max(r_max-minv[b][j],diff_reverse[b][j]));
			r_max = max(r_max,maxv[b][j]);
			b = anc[b][j];
		}
	}
	if (a == b) return max(ret, r_max - l_min);
	for (int j = k; j >= 0;--j) 
	if (anc[a][j] != -1 && anc[a][j] != anc[b][j])
	{
		int l_val = max(maxdiff[a][j], maxv[a][j] - l_min);
		int r_val = max(diff_reverse[b][j], r_max - minv[b][j]);
		ret = max(ret, max(l_val, r_val));
		l_min = min(l_min, minv[a][j]);
		r_max = max(r_max, maxv[b][j]);
		a = anc[a][j], b = anc[b][j];
	}
	int l_val = max(maxdiff[a][0], maxv[a][0] - l_min);
	int r_val = max(diff_reverse[b][0], r_max - minv[b][0]);
	ret = max(ret, max(l_val, r_val));
	l_min = min(l_min, minv[a][0]);
	r_max = max(r_max, maxv[b][0]);
	int lca = anc[a][0];
	r_max = max(r_max, c[lca]), l_min = min(l_min, c[lca]);
	return max(ret,max(0,r_max-l_min));
}

void solve()
{
	scanf("%d", &Q);
	while (Q--) {
		int u, v; scanf("%d%d", &u, &v);
		printf("%d\n", query(u, v));
	}
}

int main()
{
	while (scanf("%d", &n) == 1)
	{
		input();
		memset(anc, -1, sizeof(anc));
		memset(maxv, -1, sizeof(maxv));
		memset(minv, 0x3f, sizeof(minv));
		memset(maxdiff, 0, sizeof(maxdiff));
		memset(diff_reverse, 0, sizeof(diff_reverse));
		L[1] = 0;
		buildtree(1,-1);
		rmq();
		solve();
	}
}


 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值