zoukankan      html  css  js  c++  java
  • [学习笔记]线段树优化建边

    [算法总结]线段树优化建边

    话说ssw02很久以前就用过,当一直没有一个系统的笔记,现在ssw02来补一下

    欢迎转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11647181.html

    例题引入

    POI2015 PUS

    题面简化:

    给出一个部分未知的数列的长,以及数列已知的部分
    再给出一些区间。对于每一个区间,在它的内部钦定一些位置,并要求这些位置上的数最后的值,都严格大于区间内其他未钦定的位置上的数。
    要求给出任意一种可行的满足条件的数列。

    我们发现,选择的点和不选择的点之间有一定的等级关系,而且题目开启了 spj ,所以我们只要把等级关系放在图上然后跑拓扑即可。

    然后我们考虑到暴力建边:但这样的负载显然承受不住,N^2的边数没有任意一种算法可以计算。

    但是这道题有一个性质,我们的点都是向一个区间连接边权都为1的边。正是因为这些区间有大多数相交,才导致我们的时空复杂度都超出限制。那么我们不妨想想,有什么可以大幅提高区间操作的效率---线段树。

    然后我们考虑到线段树优化建边。建边的方式是通过一个点向一个区间连边时,将区间放在线段树中,分割为最多log区间。同时线段树的区间还要向其子区间连边。

    这幅来自洛谷的图很好的诠释了线段树优化建边的性质,只是边少,效果不明显。

    由于一个区间被分给为最多log条区间,加上我们之前线段树建图的 4N(开两倍)边,总变数不超过 NlogN 级别。

    然后跑一个拓扑排序,游戏结束

    代码 PUS

    
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 100005;
    const int MAXM = 200000+300005 ;
    int N , M , S ;
    inline int read(){
    	int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ; 
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
    }
    int head[ MAXM ] , to[ MAXM ] , nex[ MAXM ] , w[ MAXM ] , tot =1 , cnt;
    int id[ MAXN ] ;
    bool vis[MAXM];
    int du[ MAXM ] , dis[ MAXM ] , val[ MAXM ] ;
    struct Seg{
    	int ls , rs ;
    }t[ MAXN<<2 ] ;
    queue<int>q ;
    void  add( int x , int y , int z ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , w[ tot ] = z , head[ x ] = tot ;
    }
    void build( int p , int l , int r ){
        if( l == r ) {
            id[ l ] = p ;return; //反向记录 l 点在线段树中对应的节点 
        }
        int mid = (l+r)>>1 ;
        t[ p ].ls = ++cnt , du[ p ]++ ; add( cnt , p , 0 ) ; 
        t[ p ].rs = ++cnt , du[ p ]++ ; add( cnt , p , 0 ) ; 
        build( t[ p ].ls , l , mid ) ;
        build( t[ p ].rs , mid+1 , r ) ;
    }
    void updata( int p , int l , int r , int x , int y , int to ){
        if( x > y )return ;
        if( x <= l && r <= y ){
            add( p , to , 1 ) ; du[ to ]++ ;
            return;
        }
        int mid = ( l+r )>>1 ; 
        if( x <= mid )updata( t[p].ls , l , mid , x , y , to ) ;
        if( mid < y )updata( t[p].rs , mid+1 , r , x , y , to ) ;
    }
    bool toposort(){
        memset( dis , 0xcf , sizeof(dis) ) ;
        for( int i = 1 ; i <= cnt ; ++i ){
            if( du[ i ] == 0 ){
                if( val[i] )dis[ i ] = val[ i ] ;
                else dis[ i ] = 1 ;
                vis[ i ] = true ; q.push( i ) ; 
            }
        }
        while( !q.empty() ){
            int u = q.front() ; vis[ u ] = true ; q.pop() ;
            for( int i = head[ u ] ; i ; i = nex[ i ] ){
                du[ to[i] ]-- ;
                if( dis[ to[i] ] < dis[ u ] + w[ i ] )dis[ to[i] ] = dis[ u ] + w[ i ] ;
                if( du[ to[i] ] == 0 ){
                    if( val[ to[i] ] ){//我们给定的限制 
                        if( val[ to[i] ] < dis[ to[i] ]) return false;
                        else dis[ to[i] ] = val[ to[i] ] ;
                    }
                    q.push( to[i] ) ;
                }
            }
        }
        for( int i=1;i<=cnt;++i){
            if( !vis[ i ] ) return false;
            if( dis[ i ] > 1e9 ) return false;
        }
        return true;
    }
    int main(){
        N = read() , S = read() , M = read() ;int m1 , m2 , m3 ;
        ++cnt ;
        build( 1 , 1 , N ) ;
        for( int i =  1 ; i <= S ; ++i ){
            m1 = read() , m2 = read() ;
            val[ id[m1] ] = m2 ;
        }
        int las = 0 , l , r , k ;
        for( int i = 1 ; i <= M ; ++i ){
            l = read() , r = read() , k = read() , cnt++ ;
            las = l - 1 ;
            while( k-- ){
                m1 = read() ;
                updata( 1 , 1 , N , las+1 , m1-1 , cnt ) ;// p , l , r , x , y , target 
                add( cnt , id[ m1 ] , 0 ) ; 
    			du[ id[m1] ]++ , las = m1 ;
            }
            updata( 1 , 1 , N , las+1 , r , cnt ) ;
        }
        bool flag = toposort() ;//是否无解 
        if( !flag )printf("NIE") ;
        else {
            printf("TAK
    ");
            for( int i = 1 ; i <= N ; ++i )printf("%d ",dis[ id[i] ] ) ;
        }
        return 0;
    }
    
    

    然后再放一个maho烧酒馒头卡的代码,由于体面私有问题(SXK:我打死你),将这道模板放在这里。

    #include<bits/stdc++.h>
    using namespace std ;
    #define ll long long
    const int MAXN = 100005 , MAXM = 100005*18 ;
    inline int read(){
    	int s=0 ; char g=getchar() ; while( g>'9'||g<'0' )g=getchar() ; 
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
    }
    int  head[ MAXN<<2 ] , to[ MAXM ] , nex[ MAXM ] , w[ MAXM ] , tot = 1 ;
    int  N , a[ MAXN ], id[ MAXN ] , cnt = 0 , rt ;
    ll f[ MAXM ] ;
    bool vis[ MAXM ] ;
    struct Seg{
    	int ls , rs ;
    }t[ MAXN<<2 ] ;
    priority_queue< pair<ll,int> >q ; 
    void  add( int x , int y , int z ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , w[ tot ] = z , head[ x ] = tot ;
    }
    int  build( int &p , int l , int r ){
    	if( !p )p = ++cnt ;
    	if( l == r ){
    		id[ l ] = p ; return  true ;
    	}
    	int mid = ( l+r )>>1 ;
    	if( build( t[ p ].ls , l , mid ) )add( p , t[ p ].ls , a[l] ) ;
    	else add( p , t[ p ].ls , 0 ) ;
    	if( build( t[ p ].rs , mid+1 , r ) )add( p , t[ p ].rs , a[r] ) ;
    	else add(  p , t[ p ].rs  , 0 ) ;
    	return false ;
    }
    void  updata( int p , int l , int r , int x , int y , int pre ){
    	if( x <= l && r <= y ){
    		if( l == r )add( id[pre] , p , a[l]+a[pre] ) ;
    		else add( id[pre] , p , a[pre] ) ; return ;
    	}
    	int mid = ( l+r )>>1;
    	if( x <= mid )updata( t[ p ].ls , l , mid , x , y , pre ) ;
    	if( y > mid )updata( t[ p ].rs , mid+1 , r , x , y , pre ) ;
    }
    void  dijkstra(){
    	for( int i = 1 ; i <= N*18 ; ++i )f[ i ] = (1LL<<40) ; f[ id[1] ] = 0 ;
    	q.push( make_pair( 0 , id[1] ) ) ; vis[ id[1] ] = true ;
    	while( !q.empty() ){
    		int u = q.top().second ; q.pop() ; vis[ u ] = false ;
    		for( int i = head[ u ] ; i ; i = nex[ i ] )
    			if( f[ to[i] ] > f[ u ] + w[ i ] ){
    			    f[ to[i] ] = f[ u ] + w[ i ] ;
    			    if( !vis[ to[i] ] ){
    			   	    q.push( make_pair( -f[ to[i] ] , to[i] ) ) ; vis[ to[i] ] = true ;
    			    }
    			}
    	}
    }
    int main(){
    	N = read() ; int m1 , m2 ;
    	for( int i = 1 ; i <= N ; ++i )a[ i ] = read() ;
    	m1 = build( rt , 1 , N ) ; 
    	for( int i = 1 ; i <= N ; ++i ){
    		m1 = read() , m2 = read() ;
    		updata( rt , 1 , N , m1 , m2 , i ) ;
    	}
    	dijkstra() ;
    	for( int i = 1 ; i <= N ; ++i )printf("%lld
    ",(f[id[i]]==(1LL<<40))?-1:f[ id[i] ] ) ;
    	return 0 ;
    }
    

    欢迎转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11647181.html

  • 相关阅读:
    023.抓到“拔粪青年”
    JQuery给元素动态增删类或特性
    HTML元素的基本特性
    ASP.NET MVC中如何在客户端进行必要的判断
    ASP.NET MVC如何在页面加载完成后ajax异步刷新
    C#如何根据DataTable生成泛型List或者动态类型list
    C#sql语句如何使用占位符
    在html借助元素特性存储信息
    ASP.NET MVC中如何以ajax的方式在View和Action中传递数据
    如何根据集合动态构建复选框选择控件
  • 原文地址:https://www.cnblogs.com/ssw02/p/11647181.html
Copyright © 2011-2022 走看看