Red Black Tree

B - Red Black Tree

 ZOJ - 4048  

二分加 lca

关键问题在于判断过程 , 当我们选中一个答案时,我们先看看集合内有哪些数比它大,并看看能不能把这些数进行修改。

下列情况无法取该答案:

1.取到公共祖先的时候更新后的距离 仍比 选中的答案来的要大。(前提:节点与公共祖先之间没有其他红色节点)

2, 节点与祖先之间已经有红色节点了(或者这个祖先本身就是红色节点),这个时候无法更新距离,因为题目要求每个点的距离是最近红色祖先的距离,这个时候便无法更新。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2e6+10, mod = 1e9+7 , F = mod+1>>1, M = 1e8+10;

int h[N], ne[N<<1], e[N<<1], edge[N<<1], idx;
int fa[N][20], deep[N], a[N]; 
ll mi[N], de[N]; 
bool c[N];
int n, m, qq,t;
void add (int a,int b,int  c)
{
    e[idx]=b;
    ne[idx]=h[a];
    edge[idx] = c;
    h[a]=idx++;
}

void dfs(int u, int f)
{
	deep[u] = deep[f] + 1;
	for(int i = 1;i < 20;i ++) fa[u][i] = fa[fa[u][i-1]][i-1];
	for(int i = h[u];~i ;i = ne[i])
	{
		if(e[i] == f)continue;
		fa[e[i]][0] = u; 
		mi[e[i]] = c[e[i]] ? 0 : mi[u] + edge [i]; 
		de[e[i]] = de[u] + edge[i];
		dfs(e[i], u);
	}
}
int lca(int u, int v)
{
	if(deep[u]<deep[v])swap(u, v);
	int x = deep[u] - deep[v];
	for(int i = 0;i < 20;i ++)if(x>>i&1)u = fa[u][i];
	if(u == v)return v;
	for(int i = 19;i >= 0;i --)
		if(fa[u][i] != fa[v][i])
			u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}


bool cmp(int x,int y){return mi[x] < mi[y];}
bool in(int u, int v){return !c[u] && mi[v]-mi[u] == de[v] - de[u];}
bool check(ll mid,int num)
{
	int now = 0;
	for(int i = 1;i <= num;i ++)
		if(mi[a[i]] > mid) now = now ? lca(now, a[i]) : a[i];
	for(int i = 1;i <= num;i ++)
		if(mi[a[i]] > mid)
			if(!in(now, a[i]) || mi[a[i]] - mi[now] > mid)
				return 0;
	return 1;
}
int main()
{
	std::ios::sync_with_stdio(false);
	cin >> t;
	while(t--)
	{
		cin >> n >> m >> qq;
		for(int i = 1;i <= n;i ++) h[i] = -1; idx = 0; 
		for(int i = 1;i <= m;i ++)
		{
			int x;
			cin >> x;
			c[x] = 1;
		}
		for(int i = 2;i <= n;i ++)
		{
			int u, v, w;
			cin >> u >> v >> w;
			add(u, v, w); add(v, u, w);
		}
		dfs(1, 0);
	//	cout <<"azazaz" << mi[4] << endl;
		for(int i = 1 ; i <= qq ; i++)
		{
			int k; 
			cin >> k;
			for(int j = 1 ; j <= k ; j++) cin >> a[j];
			sort(a + 1,a + k + 1,cmp);
			ll l = 0 , r = 1e17;
			while(l < r)
			{
				ll mid = (l + r) >> 1;
				if(check(mid,k)) r = mid;
				else l = mid + 1;
			}
				printf("%lld\n", l);
		}


		for(int i = 1;  i <= n ; i++) c[i] = 0;
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值