[BZOJ4069]-[Apio2015]巴厘岛的雕塑-位运算的贪心dp

版权声明:转载嘛....也不是不可以(故作沉思),记得带上me的ID啊qwq https://blog.csdn.net/Izumi_Hanako/article/details/79969445

说在前面

并没有什么想说的,但是要保持格式=w=


题目

BZOJ4069传送门 这是个权限题
洛谷P3646传送门

题面就不概括了…subtask略多可以去看看


解法

首先这个题询问的是最小值,于是me一开始想到二分答案,然而并不具有连续性。如果用小于等于答案去二分,check又很麻烦
于是这个时候就要坚定一种信念!位运算是相互独立的,因此应该可以从高到低贪心的确定答案

对于那些N100的数据,明显是可以接受N3log的算法。于是我们可以这么定义:dp[i][j]表示前i个分了j组,并且每一个分组的高位与已经确定的ans相符,且当前待确定位为0,是否有一种合法方案。转移很简单。最后只需要看一看dp[N][A...B]中是否有一个为true就可以了

然而最后一个点N2000,并不能这样做。不过它有一个特殊性质,就是A=1,也就是说只要不超出分组上限就好了。因为没有下限,所以这个东西具有最优决策。定义dp[i]表示,前i个(条件和上面一样),最少分多少组。如果dp[N]B说明当前选0可行


下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , A , B ;
long long a[2005] , sum[2005] , ans ;

int g[2005] ;
bool checkSpe( int x ){
    memset( g , 0x3f , sizeof( g ) ) ; g[0] = 0 ;
    long long tmp , ww = 1LL << ( x - 1 ) ;
    for( int i = 1 ; i <= N ; i ++ ){
        for( int j = 0 ; j < i ; j ++ ){
            tmp = sum[i] - sum[j] ;
            if( ( ( tmp >> x ) | ( ans >> x ) ) != ( ans >> x ) ) continue ;
            if( !( tmp & ww ) && g[i] > g[j] )
                g[i] = g[j] + 1 ;
        }
    } return g[N] <= B ;
}

bool dp[105][105] ;
bool check( int x ){
    memset( dp , 0 , sizeof( dp ) ) ;
    dp[0][0] = true ;
    long long tmp , ww = 1LL << ( x - 1 ) ;
    for( int k = 1 ; k <= B ; k ++ ){
        for( int i = 1 ; i <= N ; i ++ ){
            for( int j = 0 ; j < i ; j ++ ){
                tmp = sum[i] - sum[j] ;
                if( ( ( tmp >> x ) | ( ans >> x ) ) != ( ans >> x ) ) continue ;
                if( !( tmp & ww ) && dp[k-1][j] ){
                    dp[k][i] = true ; break ;
                }
            }
        }
        if( k >= A && dp[k][N] ) return true ;
    } return false ;
}

void solve(){
    for( int i = 40 ; i >= 0 ; i -- )
        if( A == 1 ) ans += checkSpe( i + 1 ) ? 0 : ( 1LL << i ) ;
        else ans += check( i + 1 ) ? 0 : ( 1LL << i ) ;
    printf( "%lld" , ans ) ;
}

int main(){
    scanf( "%d%d%d" , &N , &A , &B ) ;
    for( int i = 1 ; i <= N ; i ++ )
        scanf( "%lld" , &a[i] ) , sum[i] = a[i] ;
    for( int i = 1 ; i <= N ; i ++ ) sum[i] += sum[i-1] ;
    solve() ;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页