zoukankan      html  css  js  c++  java
  • 算法浅谈之树上差分

    先放一道例题[USACO15DEC]Max Flow P

    题目大意

    给你一棵(n)个点的树,有(k)条管道,每条管道有个起始点和终结点。从起始点到终结点的路径上每个经过的点权值都要(+1)

    现在问你这(k)条管道都处理完后权值最大的点的权值是多少

    (Nle50000)

    (Kle100000)

    分析

    乍一看有一点棘手啊。

    如果是条链

    我们考虑这棵树是一条链。那么就是一个正常的差分:起始点(+1),终结点后面的点(-1),最后哪一个变量从头加到尾,记录最大值。

    其他情况

    那就只能用树上差分来做。

    树上差分。顾名思义,就是在树上进行差分操作。具体是:起始点和终止点(+1),LCA与LCA的父亲(-1)。这样就可以完成树上差分

    不会LCA的请看https://www.cnblogs.com/hulean/p/11144059.html

    具体为什么的话,画图就很明显了。

    dfs

    差分处理完后,我们只需要一遍dfs来累加每个点的值,并且维护最大值。

    这道题就做完了

    #include <bits/stdc++.h>
    using namespace std ;
    const int MAXN = 50000 + 5 ;
    struct Node {
    	int next , to ;
    } edge[ MAXN << 1 ] ;
    int head[ MAXN ] , cnt ;
    int n , k , w[ MAXN ] , ans ;
    int deep[ MAXN ] , fa[ MAXN ][ 21 ] ;
    inline int read () {
    	int tot = 0 , f = 1 ; char c = getchar () ;
    	while ( c < '0' || c > '9' ) { if ( c == '-' ) f = -1 ; c = getchar () ; }
    	while ( c >= '0' && c <= '9' ) { tot = tot * 10 + c - '0' ; c = getchar () ; }
    	return tot * f ;
    }
    inline void add ( int x , int y ) {
    	edge[ ++ cnt ].next = head[ x ] ;
    	edge[ cnt ].to = y ;
    	head[ x ] = cnt ;
    }
    inline void dfs ( int u , int father ) {
    	fa[ u ][ 0 ] = father ; deep[ u ] = deep[ father ] + 1 ;
    	for ( int i = 1 ; i <= 20 ; i ++ )
    		fa[ u ][ i ] = fa[ fa[ u ][ i - 1 ] ][ i - 1 ] ;
    	for ( int i = head[ u ] ; i ; i = edge[ i ].next ) {
    		int v = edge[ i ].to ;
    		if ( v == father ) continue ;
    		dfs ( v , u ) ;
    	}
    }
    inline int Lca ( int x , int y ) {
    	if ( x == y ) return x ;
    	if ( deep[ x ] < deep[ y ] ) swap ( x , y ) ;
    	for ( int i = 20 ; i >= 0 ; i -- ) {
    		if ( deep[ fa[ x ][ i ] ] >= deep[ y ] ) x = fa[ x ][ i ] ;
    	}
    	if ( x == y ) return x ;
    	for ( int i = 20 ; i >= 0 ; i -- ) {
    		if ( fa[ x ][ i ] != fa[ y ][ i ] ) x = fa[ x ][ i ] , y = fa[ y ][ i ] ;
    	}
    	return fa[ x ][ 0 ] ;
    }
    inline void work ( int u , int father ) {
    	for ( int i = head[ u ] ; i ; i = edge[ i ].next ) {
    		int v = edge[ i ].to ;
    		if ( v == father ) continue ;
    		work ( v , u ) ;
    		w[ u ] += w[ v ] ;
    	}
    	ans = max ( ans , w[ u ] ) ;
    }
    signed main () {
    	n = read () ; k = read () ;
    	for ( int i = 1 ; i < n ; i ++ ) {
    		int x = read () , y = read () ;
    		add ( x , y ) ; add ( y , x ) ;
    	}
    	dfs ( 1 , 0 ) ;
    	for ( int i = 1 ; i <= k ; i ++ ) {
    		int x = read () , y = read () ;
    		w[ x ] ++ ; w[ y ] ++ ;
    		int lca = Lca ( x , y ) ;
    		w[ lca ] -- ; w[ fa[ lca ][ 0 ] ] -- ;
    	}
    	work ( 1 , 0 ) ;
    	printf ( "%d
    " , ans ) ;
    	return 0 ;
    }
    
  • 相关阅读:
    Ubuntu 16.04 LTS安装好之后需要做的15件事
    双目立体视觉
    Win7、Ubuntu双系统正确卸载Ubuntu系统
    推荐一个计算机视觉图书:python计算机视觉编程
    深度学习从被监督走向互动
    详细解读神经网络十大误解,再也不会弄错它的工作原理
    不为人知的springboot的技巧
    并发情况下引发的血案
    slor6.6 在linux下的安装以及启动失败解决办法
    springmvc源码阅读2--dispatcherServlet及谈如何找源码入口
  • 原文地址:https://www.cnblogs.com/hulean/p/13360407.html
Copyright © 2011-2022 走看看