[BZOJ3714]-[PA2014]Kuglarz-这题还行

说在前面

辣鸡常数
毁me青春
费me钱财


题目

BZOJ3714传送门
看题可戳传送门


解法

可能刚开始接触这类题的时候,感觉很妙妙,所以为了不点破,并没有在标题上写出解法
如果是第一次见这种题,可以多想一想,然后再往下翻
然而见多了都是套路...









嗯好,开始说正事=w=
把题目用自己的语言说出来,是这样的:一串序列,每个位置有0/1两种取值,用最小的代价确定每个位置的取值

很容易想到的是,如果我们选取了两个区间 [l,r] [ l , r ] [l+1,r1] [ l + 1 , r − 1 ] ,那么我们就可以知道 l l r 的关系
如果可以找出一些关系链,并询问几个单点的值,那么根据这些关系链,就可以推断出所有的值。
这乍一看像是个最小生成树

但是,如果这样去找关系的话,发现能够形成关系链的选法太多了,我们必须换一种思考方式,来避免这个问题

询问一个区间,就相当于知道了它们的异或和,就相当于是知道了 sum[r]sum[l1] s u m [ r ] ⊕ s u m [ l − 1 ] sum s u m 表示异或前缀和)。如果能够知道全部的 sum s u m ,那么整个序列的值也就知道了。于是它确实是个最小生成树……


下面是卡常失败的代码

(最小生成树me写的是Boruvka算法)

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

int N ;
struct Path{
    short fr , to ;
    int val ;
} cho[2005] ;
struct Data{
    Path p[2002005] ; //C( 1001 , 2 ) = 20001000 ;
    int pcnt ;
    inline void push( const Path &A ) { p[++pcnt] = A ; }
    void clear(){ pcnt = 0 ; }
} P ;
struct Union_Set{
    int fa[2005] , siz[2005] ;
    void init(){
        for( int i = 0 ; i <= N ; i ++ )
            fa[i] = i , siz[i] = 1 ;
    } 
    int find( int x ){
        if( fa[x] == x ) return x ;
        return fa[x] = find( fa[x] ) ;
    }
    bool Union( int a , int b ){
        int Fa = find( a ) , Fb = find( b ) ;
        if( Fa == Fb ) return false ;
        if( siz[Fa] < siz[Fb] ) swap( Fa , Fb ) ;
        siz[Fa] += siz[Fb] , fa[Fb] = Fa ;
        return true ;
    }
} U ;

void solve(){
    U.init() ;
    long long ans = 0 ;
    Data *a = &P ;
    for( register int cnt = N , i ; cnt ; ){
        Path t ;
        for( i = 0 ; i <= N ; i ++ ) cho[i].val = 0x3f3f3f3f ;
        for( i = 1 ; i <= a->pcnt ; i ++ ){
            t = a->p[i] ;
            t.fr = U.find( t.fr ) , t.to = U.find( t.to ) ;
            if( t.fr == t.to ) continue ;
            if( cho[ t.fr ].val > t.val ) cho[ t.fr ] = t ;
            if( cho[ t.to ].val > t.val ) cho[ t.to ] = t ;
        }
        for( i = 0 ; i <= N ; i ++ )
            if( cho[i].val != 0x3f3f3f3f && U.Union( cho[i].fr , cho[i].to ) )
                ans += cho[i].val , cnt -- ;
    } printf( "%lld" , ans ) ;
}

inline int read_(){
    int rt = 0 ;
    char ch = getchar() ;
    while( ch < '0' || ch > '9' ) ch = getchar() ;
    while( ch >='0' && ch <='9' ) rt = ( rt << 1 ) + ( rt << 3 ) + ch - '0' , ch = getchar() ;
    return rt ;
}

int main(){
    scanf( "%d" , &N ) ;
    for( short i = 0 ; i < N ; i ++ )
    for( short j = i + 1 ; j <= N ; j ++ )
        P.push( ( Path ){ i , j , read_() } ) ;
    solve() ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值