線性基


線性基是一種數據結構,可以在\(logn\)的時間內計算出所有數的異或最大和以及異或最

小值。

1.線性基里的數都由原數異或得來

2.線性基里任意幾個數異或起來的結果都不相等。

3.線性基異或出來的結果的一個集合,與原數異或出來的集合相等(0除外,由性

質2就決定了不會有0的產生)。

4.線性基里能放下的數時有限的,只有\(log_2n\)個數

P3812 【模板】線性基

這是一道模板

void insert(ll x){
    for(int i = 52 ; i >= 0 ; i-- ){
        if( !( x >> ( 1ll * i ) ) ) continue ;
        if( !p[i] ){
            p[i] = x ;
            break ;
        }
        x = x ^ p[i] ;
    }
}

P4570 [BJWC2011]元素

題意:給你\(n\)個數,每個數有編號和權值,當編號異或起來為\(0\)時,這些權值則無

用。

題解:我們考慮從大到小排序。如果這個元素不與前面的沖突,我們就放進去。這樣貪

心為什么是對的。因為性質4。在有限的空間里我會選擇最優的進去。

P3857 [TJOI2008]彩燈

這個題就比較裸了,把所有的數放進線性基中,看線性基中有多少個數,答案就是

\(2^{size}\)

P3292 [SCOI2016]幸運數字

P3292 [SCOI2016]幸運數字

就是倍增套線性基,每個倍增里面用一個線性基去維護,合並兩個線性基就把一個線

性基里的所有元素全部插入另外一個線性基中。\(O(nlog_2^3)\)

P4151 [WC2011]最大XOR和路徑

這就時一道妙妙的題了。我們考慮把圖中所有的環的異或和求出來,如果存在一個路

徑,其中一點到其中的一個環再回來,環到這個點相當於重算了。找環的時候只要時

刻保證與\(1\)連通即可。而1到\(n\)則隨便找一條路徑即可,因為如果存在另一條路徑,

一定就構成了一個環。所以直接在線性基上找最大值就行。

CF938G Shortest Path Queries

上一題的升級版,支持加邊,刪邊,詢問\(x->y\)的最小異或路徑。非常優秀。關於

加邊和刪邊,我們可以直接套一個線段樹,然后線段樹分治就好。我們要求兩個點之

間的最小異或路徑,首先隨意保留一棵生成樹,加邊的時候判個環。有環就丟進線性

基中,類似於上面那題。求出兩點之間在樹上的一條異或路徑,最后丟進線性基里求

出最小值即可。求路徑的異或和直接用並查集(因為在樹上,每兩個點之間的路徑是

唯一的,所以我們給\(u->v\)時,直接給\(u\)的並查集的頂端向\(v\)的並查集的頂端連一

\(val[u] (xor) val[v](xor) w\)),但是我們考慮到並查集要可撤銷,就還得用按

秩合並。

這得附上代碼

#include<bits/stdc++.h>

using namespace std ;

#define ls (x<<1)
#define rs (x<<1|1)
#define N 1300050
#define LL long long

int n , m , tot , top , q ;
int fa[N] , siz[N] ;
LL val[2*N] ;
int st[2*N] , L[2*N] , R[2*N] ;
LL p[100][40] ;

map<pair<int ,int>, int> M ;
vector<int> In[4*N] ;

struct node{
    int u , v , l , r ;
    LL w ;
}e[2*N];

inline LL read()
{
    LL x = 0 , f = 1 ; char c = getchar() ;
    while( c < '0' || c > '9' ) { if( c == '-' ) f = -1 ; c = getchar() ; }
    while( c >= '0' && c <= '9' ) x = x * 10 + c - 48 , c = getchar() ;
    return x * f ;
}

int find(int x){
    if( fa[x] == x ) return x ;
    else return find( fa[x] ) ;
}

LL findval(int x){
    if( fa[x] == x ) return 0 ;
    else return val[x] ^ findval( fa[x] ) ;
}

void modify(int x , int l , int r , int ll , int rr , int k ){
    if( ll > rr ) return ;
    if( l == ll && r == rr ){
        In[x].push_back( k ) ;
        return ;
    }
    int mid = ( l + r ) >> 1 ;
    if( rr <= mid ) modify( ls , l , mid , ll , rr , k ) ;
    else if( ll > mid ) modify( rs , mid + 1 , r , ll , rr , k ) ;
    else modify( ls , l , mid , ll , mid , k ) , modify( rs , mid + 1 , r , mid + 1 , rr , k ) ; 
    return ;
}

void ins(int x , int dep){
    for(int i = 0 ; i < In[x].size() ; i++ ){
         LL now = In[x][i] ;
         int u = e[now].u , v = e[now].v ;
         int x = u , y = v ;
         LL w = e[now].w ;
         if( find(u) != find(v) ){
            u = find( u ) , v = find( v ) ;
            if( siz[u] > siz[v] ) swap( u , v ) , swap( x , y ) ;
            val[u] = findval( x ) ^ findval( y ) ^ w ;
            fa[u] = v ; st[++top] = u ;
            if( siz[u] == siz[v] ) siz[v]++ ;
        }
        else{
            LL now1 = findval( x ) ^ findval( y ) ^ w ;
            for(LL j = 30 ; j >= 0 ; j-- ){
                if( !( now1 >> j ) ) continue ;
                if( !p[dep][j] ){
                    p[dep][j] = now1 ;
                    break ;
                }
                now1 = now1 ^ p[dep][j] ;
            }
        }
    }
}

void dele(int ed ){
    while( top > ed ){
        int u = st[top] ;
        siz[fa[u]] -= siz[u] ;
        val[u] = 0 , fa[u] = u ;
        top-- ;
    }
}

void solve(int x , int l , int r , int dep ){
    if( l > r ) return ;
    int now = top ;
    ins( x , dep ) ;
    if( l == r ){
        LL ans = findval( L[l] ) ^ findval( R[l] ) ;
        for(int i = 30 ; i >= 0 ; i-- ){
            if( ( ans ^ ( p[dep][i] ) ) < ans ) ans = ans ^ p[dep][i] ;
        }
        printf("%lld\n" , ans ) ;
        dele( now ) ;
        return ;
    }
    int mid = ( l + r ) >> 1 ;
    for(int i = 30 ; i >= 0 ; i-- ) p[dep + 1][i] = p[dep][i] ;
    solve( ls , l , mid , dep + 1 ) ;
    for(int i = 30 ; i >= 0 ; i-- ) p[dep + 1][i] = p[dep][i] ;
    solve( rs , mid + 1 , r , dep + 1 ) ;
    dele( now ) ;
}

signed main()
{
    n = read() , m = read() ;
    for(int i = 1 ; i <= m ; i++ ){
        int u = read() , v = read() , w = read() ;
        e[++tot] = (node){ u , v , 1 , -1 , w } ;
        M[ make_pair( u , v ) ] = tot ;
    }
    q = read() ;
    int t = 0 ;
    while( q-- ){
        int opt , u , v , w ;
        opt = read() ;
        if( opt == 1 ){
            u = read() , v = read() , w = read() ;
            e[++tot] = (node){ u , v , t + 1 , -1 , w } ;
            M[ make_pair( u , v ) ] = tot ;
        }
        else{
            if( opt == 2 ){
                u = read() , v = read() ;
                int now = M[ make_pair( u , v ) ] ;
                e[now].r = t ;
                M[ make_pair( u , v ) ] = 0 ;
            }
            else{
                u = read() , v = read() ;
                L[++t] = u , R[t] = v ;
            }
        }
    }
    for(int i = 1 ; i <= n ; i++ ) fa[i] = i , siz[i] = 1 ;
    for(int i = 1 ; i <= tot ; i++ ) if( e[i].r == -1 ) e[i].r = t ;
    for(int i = 1 ; i <= tot ; i++ ) modify( 1 , 1 , t , e[i].l , e[i].r , i ) ;
    solve( 1 , 1 , t , 1 ) ;
    return 0 ;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM