線性基是一種數據結構,可以在\(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 ;
}