【HDU】4984 Goffi and Graph 最大生成树

传送门:【HDU】4984 Goffi and Graph


题目分析:题目中的F( i , t )表示在t时刻编号为1的点到i的所有路径中的最小边的中值最大的边的值。很明显F( i , t )代表的边一定在最大生成树上(证:假设所有到i的路径的最小边都不在最大生成树上,则部分点不连通,这与所有点构成最大生成树想矛盾,所以至少有一条最小边在最大生成树上。又因为这是最大生成树,优先添加边权大的边,所以所有到i的路径的最小边中边权最大的边一定在最大生成树上,即F( i , t )一定在最大生成树上。得证!)。也就是说如果每个时刻的最大生成树都求出来以后,则最后积分式也就可以求了。

这里我们可以发现最大生成树的F( i , t ) 发生变化当且仅当直线相交时。如果某一段时间内并没有直线相交,则这个时间段中的所有F( i , t )的积分其实就是一个个梯形的面积。因此我们需要求出所有直线相交的时间点,两个相邻的时间点就是一个时间段,因为是一次函数,所以这时边权可以用两个时间点的中点来计算,很巧的是,因为边权*两个时间点之差恰好等于一个梯形的面积,所以只要得到此时间段内属于点i的最大的最小边的边权即可。而求点i在某个时间段内的最大的最小边可以用最大生成树+dfs来求出。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
 
#define REP( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define REV( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define CLR( a , x ) memset ( a , x , sizeof a )

typedef long long LL ;

const int MAXN = 55 ;
const int MAXE = 205 ;
const double INF = 1e10 ;
const double eps = 1e-8 ;

struct Edge {
	int v ;
	double c ;
	Edge* next ;
} E[MAXE] , *H[MAXN] , *cur ;

struct Line {
	int x , y , a , b ;
	double v ;
	void input () {
		scanf ( "%d%d%d%d" , &x , &y , &a , &b ) ;
	}
	bool operator < ( const Line& p ) const {
		return v > p.v ;
	}
} L[MAXE] ;

int n , m , t ;
int p[MAXN] ;
double f[MAXN] ;
double a[10005] ;
int cnt ;
double st , ed , ans ;

int sgn ( double x ) {
	return ( x > eps ) - ( x < -eps ) ;
}

void clear () {
	cur = E ;
	CLR ( H , 0 ) ; 
	FOR ( i , 1 , n ) {
		p[i] = i ;
		f[i] = INF ;
	}
}

void addedge ( int u , int v , double c ) {
	cur -> v = v ;
	cur -> c = c ;
	cur -> next = H[u] ;
	H[u] = cur ++ ;
}

int find ( int x ) {
	return p[x] == x ? x : ( p[x] = find ( p[x] ) ) ;
}

void kruskal () {
	sort ( L , L + m ) ;
	int cnt = 1 ;
	REP ( i , 0 , m ) {
		int x = find ( L[i].x ) ;
		int y = find ( L[i].y ) ;
		if ( x != y ) {
			p[x] = y ;
			addedge ( L[i].x , L[i].y , L[i].v ) ;
			addedge ( L[i].y , L[i].x , L[i].v ) ;
			if ( ++ cnt == n ) break ;
		}
	}
}

void dfs ( int u , int fa ) {
	travel ( e , H , u ) {
		int v = e -> v ;
		if ( v == fa ) continue ;
		f[v] = min ( f[u] , e -> c ) ;
		dfs ( v , u ) ;
	}
}

int unique ( int n ) {
	int cnt = 1 ;
	sort ( a + 1 , a + n + 1 ) ;
	FOR ( i , 2 , n ) if ( sgn ( a[i] - a[cnt] ) ) a[++ cnt] = a[i] ;
	return cnt ;
}

void solve () {
	ans = 0 ;
	cnt = 0 ;
	scanf ( "%d%d%d" , &n , &m , &t ) ;
	REP ( i , 0 , m ) {
		L[i].input () ;
		REP ( j , 0 , i ) {
			if ( L[i].b == L[j].b ) continue ;
			a[++ cnt] = ( double ) ( L[i].a - L[j].a ) / ( L[j].b - L[i].b ) ;
		}
	}
	a[++ cnt] = t ;
	cnt = unique ( cnt ) ;
	st = 0 ;
	FOR ( i , 1 , cnt ) {
		if ( a[i] < 0 ) continue ;
		if ( a[i] > t ) break ;
		ed = a[i] ;
		double mid = ( st + ed ) / 2 ;
		REP ( j , 0 , m ) L[j].v = L[j].a + L[j].b * mid ;
		clear () ;
		kruskal () ;
		dfs ( 1 , 0 ) ;
		FOR ( j , 2 , n ) ans += f[j] * ( ed - st ) ;
		st = ed ;
	}
	printf ( "%.3f\n" , ans / t ) ;
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值