【BZOJ2599】[IOI2011]Race 树的点分治

【BZOJ2599】[IOI2011]Race

Description

给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000

Input

第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)

Output

一个整数 表示最小边数量 如果不存在这样的路径 输出-1

Sample Input

4 3
0 1 1
1 2 2
1 3 4

Sample Output

2

题解:本题大部分代码都与POJ1741那道模板题相同,只不过是calc函数里有些不一样

如果我们想计算一棵子树里的答案,仍然是先按照每个点到根的距离排序,然后用双指针法算出长度=m的路径,那么问题来了,我们怎样将答案中的非简单路径去掉呢?

由于我们最终要求的是经过边最少的长度=m的简单路径,那么我们可以用ans[i]保存经过i条边的路径数,然后在calc的时候直接修改ans就行了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=200010;
int to[maxn<<1],next[maxn<<1],val[maxn<<1],dep[maxn],s[maxn],head[maxn],siz[maxn];
int vis[maxn],ans[maxn],p[maxn];
int n,m,tot,cnt,root,maxx;

bool cmp(int a,int b)
{
	return s[a]<s[b];
}
void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void getroot(int x,int fa)
{
	int i,mx=0;
	siz[x]=1;
	for(i=head[x];i!=-1;i=next[i])
	{
		if(to[i]==fa||vis[to[i]])	continue;
		getroot(to[i],x);
		siz[x]+=siz[to[i]];
		mx=max(mx,siz[to[i]]);
	}
	mx=max(mx,tot-siz[x]);
	if(maxx>mx)	root=x,maxx=mx;
}
void getdep(int x,int fa)
{
	p[++p[0]]=x;
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(to[i]==fa||vis[to[i]])	continue;
		s[to[i]]=s[x]+val[i],dep[to[i]]=dep[x]+1;
		getdep(to[i],x);
	}
}
void calc(int x,int flag)
{
	p[0]=0,getdep(x,0);
	sort(p+1,p+p[0]+1,cmp);
	int l=1,r=p[0],i;
	for(;l<r;l++)
	{
		while(l<r&&s[p[l]]+s[p[r]]>m)	r--;
		for(i=r;i>l&&s[p[l]]+s[p[i]]==m;i--)	ans[dep[p[l]]+dep[p[i]]]+=flag;
	}
}
void dfs(int x)
{
	vis[x]=1;
	s[x]=dep[x]=0,calc(x,1);
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]])	continue;
		s[to[i]]=val[i],dep[to[i]]=1,calc(to[i],-1);
		maxx=1<<30,tot=siz[to[i]],getroot(to[i],x);
		dfs(root);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int i,a,b,c;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)
	{
		scanf("%d%d%d",&a,&b,&c),a++,b++;
		add(a,b,c),add(b,a,c);
	}
	maxx=1<<30;
	getroot(1,0);
	dfs(1);
	for(i=1;i<=n;i++)
	{
		if(ans[i])
		{
			printf("%d\n",i);
			return 0;
		}
	}
	printf("-1");
	return 0;
}
时间: 2024-10-13 04:50:57

【BZOJ2599】[IOI2011]Race 树的点分治的相关文章

bzoj2599: [IOI2011]Race(点分治)

写了四五道点分治的题目了,算是比较理解点分治是什么东西了吧= = 点分治主要用来解决点对之间的问题的,比如距离为不大于K的点有多少对. 这道题要求距离等于K的点对中连接两点的最小边数. 那么其实道理是一样的.先找重心,然后先从重心开始求距离dis和边数num,更新ans,再从重心的儿子开始求得dis和num,减去这部分答案 因为这部分的答案中,从重心开始的两条链有重叠部分,所以要剪掉 基本算是模板题,但是减去儿子的答案的那部分还有双指针那里调了好久,所以还不算特别熟练.. PS跑了27秒慢到飞起

【点分治】【哈希表】bzoj2599 [IOI2011]Race

给nlog2n随便过的跪了,不得已弄了个哈希表伪装成nlogn(当然随便卡,好孩子不要学)…… 不过为啥哈希表的大小开小点就RE啊……?必须得超过数据范围一大截才行……谜 #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int f,c; inline void R(int &x){ c=0;f=1; for(;c<'0'||c>'9';c=getc

bzoj1758 [Wc2010]重建计划 &amp; bzoj2599 [IOI2011]Race

两题都是树分治. 1758这题可以二分答案avgvalue,因为avgvalue=Σv(e)/s,因此二分后只需要判断Σv(e)-s*avgvalue是否大于等于0,若大于等于0则调整二分下界,否则调整二分上界.假设一棵树树根为x,要求就是经过树根x的最大答案,不经过树根x的可以递归求解.假设B[i]为当前做到的一颗x的子树中的点到x的距离为i的最大权值,A[i]为之前已经做过的所有子数中的点到x的距离为i的最大权值(这里的权值是Σv(e)-i*avgvalue),那么对于当前子树的一个距离i,

[BZOJ2599][IOI2011]Race

试题描述 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 输入 第一行 两个整数 n, k第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始) 输出 一个整数 表示最小边数量 如果不存在这样的路径 输出-1 输入示例 4 3 0 1 1 1 2 2 1 3 4 输出示例 2 数据规模及约定 见“试题描述” 题解 点分治裸题.我还调了半天TAT...好久没写什么都忘了... 每次找子树的中心往下递

BZOJ 2599: [IOI2011]Race( 点分治 )

数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新CNT数组和答案. ------------------------------------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; typ

【BZOJ-2599】Race 点分治

2599: [IOI2011]Race Time Limit: 70 Sec  Memory Limit: 128 MBSubmit: 2590  Solved: 769[Submit][Status][Discuss] Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 Input 第一行 两个整数 n, k第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始) Output

hdu_5314_Happy King(树的点分治)

题目链接:hdu_5314_Happy King 题意: 给出一颗n个结点的树,点上有权值: 求点对(x,y)满足x!=y且x到y的路径上最大值与最小值的差<=D: 题解: 还是树的点分治,在统计答案的时候先按到根的最小值排序,然后用最大值减D去找有多少个满足答案. 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 typedef pair<i

bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 &amp;&amp; AC400

3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec  Memory Limit: 512 MBSubmit: 159  Solved: 40[Submit][Status][Discuss] Description 强 强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,

bzoj 2152: 聪聪可可 树的点分治

2152: 聪聪可可 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 485  Solved: 251[Submit][Status] Description 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏.他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“