BZOJ-1495: [NOI2006]网络收费 (状压DP)

作者: acccccc | 来源:发表于2019-03-15 09:54 被阅读0次

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1495

首先可以很容易的把贡献分开处理成每一个节点对LCA的贡献,然后考虑DP,我们发现每个状态如果包括叶子的状态的话太大,那么变成包括祖先的状态,然后从下往上递推,dp(i,j,k)表示节点i,包括了j个A节点,祖先状态为k(状压)的最优解,然后递推即可,这里的状态是稀疏的,我们可以数学方法算出状态数为O(2(2n))级别,转移开销是O(n2(2n))级别,然后用一个HASH来存即可。

代码(改了N久HASH才终于不MLE+TLE了QAQ):

#include <cstdio>

#include <algorithm>

#include <cstring>

#include <vector>

  

using namespace std ;

  

#define DOWN( i , r , l ) for ( int i = r ; i >= l ; -- i )

#define Rep( i , x ) for ( int i = 0 ; i < x ; ++ i )

#define rep( i , x ) for ( int i = 0 ; i ++ < x ; )

#define REP( i , l , r ) for ( int i = l ; i <= r ; ++ i )

  

typedef long long ll ;

  

const int maxs = 1015043 ;

const int maxn = 10 ;

  

struct node {

    int i , j , k , v ;

    node( int _i , int _j , int _k , int _v ) : i( _i ) , j( _j ) , k( _k ) , v( _v ) {

    }

} ;

  

struct HASH {

    vector < node > V[ maxs ] ;

    inline void Init(  ) {

        Rep( i , maxs ) V[ i ].clear(  ) ;

    }

    inline void add( int i , int j , int k , int v ) {

        int p = ( ll ) i * 117191 % maxs + j * 311123 % maxs + k ; p %= maxs ;

        int s = V[ p ].size(  ) ;

        Rep( h , s ) if ( V[ p ][ h ].i == i && V[ p ][ h ].j == j && V[ p ][ h ].k == k ) {

            if ( v < V[ p ][ h ].v ) V[ p ][ h ].v = v ; return ;

        }

        V[ p ].push_back( node( i , j , k , v ) ) ;

    }

    inline int ask( int i , int j , int k ) {

        int p = ( ll ) i * 117191 % maxs + j * 311123 % maxs + k ; p %= maxs ;

        int s = V[ p ].size(  ) ;

        Rep( h , s ) if ( V[ p ][ h ].i == i && V[ p ][ h ].j == j && V[ p ][ h ].k == k ) {

            return V[ p ][ h ].v ;

        }

        return 1000000000 ;

    }

} dp[ 2 ] ;

  

int n , N , C[ 1 << maxn ] , D[ 1 << maxn ] , f[ 1 << maxn ][ 1 << maxn ] ;

int ht[ 1 << ( maxn + 1 ) ] , pt = 0 ;

  

int ch ;

  

inline void getint( int &t ) {

    for ( ch = getchar(  ) ; ch < '0' || ch > '9' ; ch = getchar(  ) ) ;

    t = ch - '0' ;

    for ( ch = getchar(  ) ; ch >= '0' && ch <= '9' ; ch = getchar(  ) ) {

        t = t * 10 + ch - '0' ;

    }

}

  

int g[ 2 ][ 2 ][ 1 << maxn ] ;

  

inline void getg( int x , int i ) {

    int a , b ;

    Rep( j , N ) {

        a = 0 , b = 0 ;

        for ( int k = ( N + i ) >> 1 , h = 1 ; k ; k >>= 1 , h <<= 1 ) {

            if ( j & h ) b += f[ i ][ k ] ; else a += f[ i ][ k ] ;

        }

        if ( C[ i ] ) a += D[ i ] ; else b += D[ i ] ;

        g[ x ][ 1 ][ j ] = a , g[ x ][ 0 ][ j ] = b ;

    }

}

  

int main(  ) {

    getint( n ) ; N = 1 << n ;

    Rep( i , N ) getint( C[ i ] ) ;

    Rep( i , N ) getint( D[ i ] ) ;

    int x , a , b ;

    memset( f , 0 , sizeof( f ) ) ;

    Rep( i , N ) REP( j , ( i + 1 ) , ( N - 1 ) ) {

        getint( x ) ;

        for ( a = N + i , b = N + j ; a != b ; a >>= 1 , b >>= 1 ) ;

        f[ i ][ a ] += x , f[ j ][ a ] += x ;

    }

    ht[ 1 ] = 0 ;

    REP( i , 1 , ( N - 1 ) ) ht[ i << 1 ] = ht[ i ] + 1 , ht[ ( i << 1 ) ^ 1 ] = ht[ i ] + 1 ;

    int rec ;

    REP( i , ( N >> 1 ) , ( N - 1 ) ) {

        getg( 0 , ( i << 1 ) - N ) , getg( 1 , ( i << 1 ) + 1 - N ) ;

        int st ;

        REP( j , 0 , 2 ) Rep( k , ( 1 << ht[ i ] ) ) {

            st = ( k << 1 ) ^ ( j >= 1 ) ;

            REP( h , 0 , j ) {

                a = h < 2 ? g[ 0 ][ h ][ st ] : 1000000000 ;

                b = ( j - h ) < 2 ? g[ 1 ][ j - h ][ st ] : 1000000000 ;

                dp[ pt ].add( i , j , k , a + b ) ;

            }

        }

    }

    DOWN( i , ( ( N >> 1 ) - 1 ) , 1 ) {

        int sz = 1 << ( n - ht[ i ] ) , fg , st ;

        REP( j , 0 , sz ) Rep( k , ( 1 << ht[ i ] ) ) {

            fg = j >= ( sz - j ) ;

            st = ( k << 1 ) ^ fg ;

            REP( h , 0 , min( j , sz >> 1 ) ) {

                a = dp[ pt ].ask( i << 1 , h , st ) , b = dp[ pt ].ask( ( i << 1 ) ^ 1 , j - h , st ) ;

                dp[ pt ^ 1 ].add( i , j , k , a + b ) ;

            }

        }

        if ( i == 1 || ht[ i ] != ht[ i - 1 ] ) {

            dp[ pt ].Init(  ) ; pt ^= 1 ;

        }

    }

    int ans = 0x7fffffff ;

    REP( i , 0 , N ) ans = min( ans , dp[ pt ].ask( 1 , i , 0 ) ) ;

    printf( "%d\n" , ans ) ;

    return 0 ;

}

相关文章

  • BZOJ-1495: [NOI2006]网络收费 (状压DP)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1495 首...

  • 状压DP

    最短Hamilton路径 原题链接[https://www.acwing.com/activity/content...

  • DP训练——状压DP

    状压DP BZOJ1087题意在的棋盘里面放个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以...

  • 状压DP系列

    几点注意: 1.数组下标从1开始比较方便 zoj Easy 2048 Again保存状态的时候是保存下降子序列的情...

  • LeetCode 状压dp

    5639. 完成所有工作的最短时间[https://leetcode-cn.com/problems/find-m...

  • 状态压缩和状压DP

    问题:n*n的棋盘放置n个点,保证每一行,每一列都有且只有一个点,有几种放置方式? 一、组合数解法:ans=n!二...

  • POJ 3311 floyd+压状DP

    poj3311因为这道题 点N 不超过10 可以 把状态转化 为 二进制数,0表示没经过这个点,1表示经过这个点。...

  • 图论(2)-从动态规划到网络流

    今天这期对LC比赛来说有点超纲。因为一般LC出这类题的话,你能够用状压DP或者其他手段去解决的。而网络流是能够处理...

  • HDU-5816 状压DP [2016多校]

    桌面有N张A型牌,M张B型牌,目前玩家可抽一张牌(盲抽),若抽到A牌则可再抽两张,若抽到B牌,则可减少对方若干生命...

  • 状压DP——二进制的妙用

    之前我们讲解了背包问题、树形DP,区间DP这三类问题。这些都是中规中矩的动态规划题目。今天,我为大家讲解一种比较有...

网友评论

    本文标题:BZOJ-1495: [NOI2006]网络收费 (状压DP)

    本文链接:https://www.haomeiwen.com/subject/rhokmqtx.html