bzoj3784

3784: 树上的路径

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 511   Solved: 171
[ Submit][ Status][ Discuss]

Description

给定一个N个结点的树,结点用正整数1..N编号。每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路边上经过边的权值。其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值。

Input

第一行两个正整数N,M
下面N-1行,每行三个正整数a,b,c(a,b<=N,C<=10000)。表示结点a到结点b有一条权值为c的边。

Output

共M行,如题所述.

Sample Input

5 10
1 2 1
1 3 2
2 4 3
2 5 4

Sample Output

7
7
6
5
4
4
3
3
2
1

HINT

N<=50000,M<=Min(300000,n*(n-1) /2 )


Source



题解:

我的算法好像又和网上的不一样。。。

%peehs一眼秒了这题。

先二分长度小于等于ans的路径有几条。

这个可以用树分治做,为了方便,用边分治。分治时暴力枚举被切开的边的一边的点,另一边二分找有几个满足要求的。

时间复杂度O(nlog^3n)。

考虑把第二个二分去掉。先预处理出边分治的结果(把两个到根的距离数组排序),之后枚举一边的点时,另一边是单调的,所以一起扫一遍就可以了。

时间复杂度O(nlog^2n),空间复杂度O(nlogn)。




#include <stdio.h>
#include <algorithm>

using namespace std;

//long long
const long long maxn = 51000;
const long long maxm = 310000;
struct so {
	long long l[3] , r[3] , f , len;
} s[maxn*18];
struct node {
	long long v , taboo , c;
	node *next , *rev;
} poolo[maxn*5] , pool[maxn*5] , *og[maxn*2] , *g[maxn*2];
long long topo , top , topver , topa;
long long n , m;
long long next[maxn*2];
long long a[maxn*50] , index;
long long siz[maxn*2];
long long ans[maxm] , topans;
bool cmp ( long long x1 , long long x2 ) {
	return x1 > x2;
}
void addo ( long long u , long long v , long long c ) {
	node *tmp1 = &poolo[++topo] , *tmp2 = &poolo[++topo];
	tmp1 -> v = v; tmp1 -> c = c; tmp1 -> next = og[u]; og[u] = tmp1; tmp1 -> rev = tmp2;
	tmp2 -> v = u; tmp2 -> c = c; tmp2 -> next = og[v]; og[v] = tmp2; tmp2 -> rev = tmp1;
}
void add ( long long u , long long v , long long c ) {
	//printf ( "%lld %lld %lld\n" , u , v , c );
	node *tmp1 = &pool[++top] , *tmp2 = &pool[++top];
	tmp1 -> v = v; tmp1 -> c = c; tmp1 -> next = g[u]; g[u] = tmp1; tmp1 -> rev = tmp2;
	tmp2 -> v = u; tmp2 -> c = c; tmp2 -> next = g[v]; g[v] = tmp2; tmp2 -> rev = tmp1;
}
void rebuild ( long long i , long long from ) {
	for ( node *j = og[i] ; j ; j = j -> next ) if ( j -> v != from ) {
		//printf ( "%d  %d\n" , i , j -> v );
		add ( next[i] , ++topver , 0 );
		add ( topver , j -> v , j -> c );
		next[i] = topver;
		rebuild ( j -> v , i );
	}
}
void dfs1 ( long long i , long long from ) {
	siz[i] = 1;
	for ( node *j = g[i] ; j ; j = j -> next ) if ( j -> v != from & j -> taboo == 0 ) {
		dfs1 ( j -> v , i );
		siz[i] += siz[j->v];
	}
}
node *getroot ( long long i , long long from , long long all , node *ret ) {
	node *tmp = NULL;
	for ( node *j = g[i] ; j ; j = j -> next ) if ( j -> v != from && j -> taboo == 0 ) {
		if ( siz[j->v] * 2 >= all ) return getroot ( j -> v , i , all , j );
		else {
			if ( tmp == NULL || siz[tmp->v] < siz[j->v] ) tmp = j;
		}
	}
	if ( ret == NULL ) return tmp;
	else return ret;
}
void dfs2 ( long long i , long long from , long long sum ) {
	if ( i <= n ) a[++topa] = sum;
	for ( node *j = g[i] ; j ; j = j -> next ) if ( j -> v != from && j -> taboo == 0 ) {
		dfs2 ( j -> v , i , sum + j -> c );
	}
}
void split ( long long i ) {
	dfs1 ( i , -1 );
	if ( siz[i] == 1 ) return ;
	node *tmp = getroot ( i , -1 , siz[i] , NULL );
	//printf ( "%lld %lld %lld    %lld \n" , i , siz[i] , tmp -> v , tmp -> rev -> v );
	tmp -> taboo = 1; tmp -> rev -> taboo = 1;
	++index;
	s[index].len = tmp -> c;
	s[index].l[1] = topa + 1;
	dfs2 ( tmp -> v , -1 , 0 );
	s[index].r[1] = topa;
	s[index].l[2] = topa + 1;
	dfs2 ( tmp -> rev -> v , -1 , 0 );
	s[index].r[2] = topa;
	//printf ( "%lld %lld %lld %lld %lld len%lld\n" , index , s[index].l[1] , s[index].r[1] , s[index].l[2] , s[index].r[2] , s[index].len);
	if ( s[index].l[1] > s[index].r[1] || s[index].l[2] > s[index].r[2] ) s[index].f = 0;
	else {
		s[index].f = 1;
		sort ( a + s[index].l[1] , a + 1 + s[index].r[1] , cmp );
		sort ( a + s[index].l[2] , a + 1 + s[index].r[2] , cmp );
	}
	//for ( i = s[index].l[1] ; i <= s[index].r[1] ; i++ ) printf ( "%d " , a[i] );
	//printf ( "\n" );
	//for ( i = s[index].l[2] ; i <= s[index].r[2] ; i++ ) printf ( "%d " , a[i] );
	//printf ( "\n" );
	//printf ( "%d\n" , s[index].f );
	split ( tmp -> v ); split ( tmp -> rev -> v );
}
long long check ( long long x ) {
	long long i , j , k , sum = 0;
	for ( i = 1 ; i <= index ; i++ ) {
		if ( s[i].f == 0 ) continue;
		k = s[i].r[2];
		for ( j = s[i].l[1] ; j <= s[i].r[1] ; j++ ) {
			while ( k > s[i].l[2] && a[k] + a[j] + s[i].len < x ) k--;
			if ( a[k] + a[j] + s[i].len >= x ) sum += k - s[i].l[2] + 1;
			//printf ( "%lld %lld\n" , s[i].l[2] , x );
		}
	}
	return sum;
}
void find ( long long x ) {
	//printf ( "%lld\n" ,x );
	long long i , j , k;
	for ( i = 1 ; i <= index ; i++ ) {
		if ( s[i].f == 0 ) continue;
		for ( j = s[i].l[1] ; j <= s[i].r[1] ; j++ ) {
			for ( k = s[i].l[2] ; k <= s[i].r[2] ; k++ ) {
				if ( a[j] + a[k] + s[i].len <= x ) break;
				//if ( i == 22 ) printf ( "## %lld\n" , a[j] + a[k] + s[i].len );
				ans[++topans] = a[j] + a[k] + s[i].len;
			}
		}
	}
}
void work () {
	long long i , u , v , c , l , r , mid , x;
	scanf ( "%lld%lld" , &n , &m );
	for ( i = 1 ; i < n ; i++ ) {
		scanf ( "%lld%lld%lld" , &u , &v , &c );
		addo ( u , v , c );
	}
	for ( i = 1 ; i <= n ; i++ ) next[i] = i;
	topver = n;
	rebuild ( 1 , 0 );
	split ( 1 );
	l = 0; r = 510000000;
	while ( l < r - 1 ) {
		mid = (l+r)/2;
		if ( check ( mid ) < m ) r = mid;
		else l = mid;
	}
	topans = 0;
	if ( check ( l ) < m ) find ( r ), x = r;
	else find ( l ), x = l;
	for ( i = topans + 1 ; i <= m ; i++ ) ans[i] = x;
	sort ( ans + 1 , ans + 1 + m );
	for ( i = m ; i >= 1 ; i-- ) printf ( "%lld\n" , ans[i] );
}
int main () {
	//FILE *fpr = freopen ( "bzoj3784.in" , "r" , stdin );
	//FILE *fpw = freopen ( "bzoj3784.out" , "w" , stdout );
	work ();
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值