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