qoj 1168 题解及线性基相关探究
简要题意
给定 \(n\) 个点 \(m\) 条边的无向联通图 \(G\),边有边权。其中 \((u,v)\) 的距离 \(d(u,v)\) 定义为 \(u\to v\) 的最大 \(\text{xor}\) 路径。
\(q\) 次询问 \(l,r\),求 \(\bigoplus\limits_{l\le i<j\le r}d(i,j)\) 的值。
\(1\le n,m,q\le 10^5,0\le w<2^{30}\)。
一些转化
首先必然套用 P4151 的结论:设所有环的异或和构成空间为 \(V\)。\(d'(u,v)\) 为 \(u\to v\) 的任意一条路径的异或和。
则 \(d(u,v)=\max\{d'(u,v)\oplus x\mid x\in V\}\)。考虑记变换 \(F_V(x)=\max\{x\oplus a\mid a\in V\},G_V(x)=\min\{x\oplus a\mid a\in V\}\)。
\(d(u,v)=F_V(d'(u,v))\),由于一些深刻的理论,我们接下来对 \(F,G\) 的关系进行探究。
线性基上的 min/max 探究
我们给出如下结论:
-
\(F_V(x)\oplus F_V(y)=G_V(x\oplus y)\)
-
\(G_V(x)\oplus G_V(y)=G_V(x\oplus y)\)
-
\(F_V(x)\oplus G_V(y)=F_V(x\oplus y)\)
简而言之:
$\oplus $ | \(F\) | \(G\) |
---|---|---|
\(F\) | \(F\) | \(G\) |
\(G\) | \(G\) | \(F\) |
下面证明 \(F\oplus F\to G\),其他同理。下面过程为了迎合 oi 中的线性基算法,可能有少许不严谨。
记 \(V\) 的线性基的构成元素为 \(B_0,B_1,\cdots,B_L\),但是这里我们令 \(B_L\) 有二进制最高位 \(L\),并把剩下缺的 \(B\) 补齐为 \(0\)。
考察 \(F_V(x)\) 运算在 \(L\to 0\) 的每一位中的决策,这对于每一位是一致的,即:
当第 \(k\) 位为 \(0\) 时,异或上 \(B_k\)。当第 \(k\) 位为 \(1\) 时,不异或上 \(B_k\)。
\(G\) 则是:当第 \(k\) 位为 \(0\) 时,不异或上 \(B_k\)。当第 \(k\) 位为 \(1\) 时,异或上 \(B_k\)。
我们把 \(F_V(x)\) 和 \(F_V(y)\) 在第 \(k\) 位的决策合并起来:
\(F\oplus F\):当 \(x\oplus y\) 第 \(k\) 位为 \(0\) 时,不异或上 \(B_k\)。当第 \(k\) 位为 \(1\) 时,异或上 \(B_k\)。
我们就得出了 \(G_V(x\oplus y)\)。
更进一步的,显然我们能基于上面三个推出:
\(F^a\oplus G^b\to \begin{cases}G(2\mid a)\\F(2\nmid a)\end{cases}\)
并且,我们注意到 \(G\) 是一个线性变换,即根据 \(G_V(x)\oplus G_V(y)=G_V(x\oplus y)\)。这将很有用。
原题解法
首先容易根据前置知识求出 \(\{a\},V\),其中 \(V\) 为线性基,\(a\) 满足:\(d'(u,v)=a_u \oplus a_v\)。
根据上述理论,我们有:\(F_V(x)=F_V(x\oplus 0)=F_V(0)\oplus G_V(x)\)。
于是 \(\bigoplus\limits_{l\le i<j\le r}F_V(d'(i,j))=\bigoplus\limits_{l\le i<j\le r}F_V(0)\oplus \bigoplus\limits_{l\le i<j\le r}G_V(d'(i,j))=\bigoplus\limits_{l\le i<j\le r}F_V(0)\oplus G_V\left(\bigoplus\limits_{l\le i<j\le r}d'(i,j)\right)\)
于是单次询问就容易处理了,上个线性基板子即可。
$\texttt{code}$
//qoj 1168
//https://qoj.ac/contest/537/problem/1168
#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=1e5+5;
struct edge{int to,nex,w;}e[N<<1];
int n,m,q,tot,head[N],B[31],a[N];bool v[N];
inline void add(int u,int v,int w)
{
e[++tot]={v,head[u],w};head[u]=tot;
e[++tot]={u,head[v],w};head[v]=tot;
}
inline void ins(int x)
{
for(int i=30;i>=0;i--)
if((x>>i)&1)
{
if(!B[i]){B[i]=x;break;}
x^=B[i];
}
}
inline int que(int x){for(int i=30;i>=0;i--) if((x^B[i])<x) x^=B[i];return x;}
void dfs(int x,int s)
{
a[x]=s;v[x]=1;
for(int i=head[x];i;i=e[i].nex)
{
int to=e[i].to;
!v[to]?dfs(to,s^e[i].w):ins(s^a[to]^e[i].w);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int u,v,w;m--;) scanf("%d%d%d",&u,&v,&w),add(u,v,w);
dfs(1,0);int mx=0;
for(int i=30;i>=0;i--) if((mx^B[i])>mx) mx^=B[i];
for(int i=1;i<=n;i++) a[i]^=a[i-1];
for(int l,r;q--;)
{
scanf("%d%d",&l,&r);int x=r-l,ans=0;
if(x&1) ans^=que(a[r]^a[l-1]);
if(((LL)x*(x+1)/2)&1) ans^=mx;printf("%d\n",ans);
}
return 0;
}