线性基

线性基是一种数据结构,可以在\(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 ;
}

转载于:https://www.cnblogs.com/powerYao/p/11445247.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值