BZOJ 2599 IOI 2011 Race 树的分治

题目大意:给一棵树,每条边有权。求一条路径,权值和等于K,且边的数量最小。


思路:BZ上没写数据范围,看了别人的博客发现是20w的点数。与正常的树分治的题不太一样,这个题每次统计答案不能只返回一个最值,而是要把所有的状态都存起来,方便在子树中删除的时候删掉。所以就有一个ans[i],表示路径数量是i的时候,答案的数量。其他的这个题的小细节特别多,一个函数传5个参伤不起啊。。。


CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 200010
#define INF 0x3f3f3f3f
using namespace std;

struct Complex{
	int dis,step;
	bool operator <(const Complex &a)const {
		return dis < a.dis;
	}
}temp[MAX];

int points,k;
int head[MAX],total;
int next[MAX << 1],aim[MAX << 1],length[MAX << 1];

bool v[MAX];
int root,size[MAX],_size,_total;
int p,cnt[MAX];

int ans[MAX];

inline void Add(int x,int y,int len);

void Work(int x);
void GetRoot(int x,int last);
inline void Count(int x,int last,int len,int c,int step);
void GetDis(int x,int last,int len,int step);

int main()
{
	cin >> points >> k;
	for(int x,y,z,i = 1;i < points; ++i) {
		scanf("%d%d%d",&x,&y,&z);
		x++,y++;
		Add(x,y,z),Add(y,x,z);
	}
	Work(1);
	int flag = -1;
	for(int i = 1;i < MAX; ++i)
		if(ans[i]) {
			flag = i;
			break;
		}
	cout << flag << endl;
	return 0;
}

inline void Add(int x,int y,int len)
{
	next[++total] = head[x];
	aim[total] = y;
	length[total] = len;
	head[x] = total;
}

void Work(int x)
{
	_size = INF,_total = size[x] ? size[x]:points;
	GetRoot(x,0);
	x = root;
	v[x] = true;
	Count(x,0,0,1,0);
	for(int i = head[x];i;i = next[i]) {
		if(v[aim[i]])	continue;
		Count(aim[i],x,length[i],-1,1);
		Work(aim[i]);
	}
}

void GetRoot(int x,int last)
{
	size[x] = 1;
	int max_size = 0;
	for(int i = head[x];i;i = next[i]) {
		if(v[aim[i]] || aim[i] == last)	continue;
		GetRoot(aim[i],x);
		size[x] += size[aim[i]];
		max_size = max(max_size,size[aim[i]]);
	}
	max_size = max(max_size,_total - size[x]);
	if(max_size < _size)
		_size = max_size,root = x;
}

inline void Count(int x,int last,int len,int c,int step)
{
	p = 0;
	GetDis(x,last,len,step);
	sort(temp + 1,temp + p + 1);
	int l = 1,r = p;
	while(l < r) {
		if(temp[l].dis + temp[r].dis > k)	--r;
		else if(temp[l].dis + temp[r].dis < k)	++l;
		else {
			int _l = l;
			while(temp[_l + 1].dis == temp[l].dis)	++_l;
			int _r = r;
			while(temp[_r - 1].dis == temp[r].dis)	--_r;
			for(int i = l;i <= _l; ++i)
				for(int j = _r;j <= r; ++j)
					if(i != j)
						ans[temp[i].step + temp[j].step] += c;
			l = _l + 1,r = _r - 1;
		}
	}
}

void GetDis(int x,int last,int len,int step)
{
	temp[++p].dis = len;
	temp[p].step = step;
	for(int i = head[x];i;i = next[i]) {
		if(aim[i] == last || v[aim[i]])	continue;
		GetDis(aim[i],x,len + length[i],step + 1);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值