將區間拆分爲 \([x2^i,(x+1)2^i)\) 的形式,發現兩個區間中的數兩兩異或後造成的仍爲一個區間,將 \(A,B\) 都拆分後區間兩兩異或會獲得 \(O(n^2\log^2n)\) 個區間,取並即爲答案,但複雜度沒法接受。node
發現對於兩個區間 \([x2^i,(x+1)2^i),[y2^j,(y+1)2^j),i>j\),其異或後獲得的結果還是高位固定,後 \(i\) 位任意取,也就是第二個區間中有一段是不用考慮的,這是由於這一段在第一個區間中是任意取的。在線段樹上就是隻用考慮同一深度的區間,這樣獲得的區間個數就爲 \(O(n^2\log n)\) 了。c++
就用線段樹來實現便可,\(dfs\) 的過程當中記錄當前高位的異或值,當有區間被徹底覆蓋後就返回。git
#include<bits/stdc++.h> #define maxn 100010 #define maxm 62 #define p 998244353 #define inv2 499122177 #define mid ((l+r)>>1) #define mk make_pair using namespace std; typedef long long ll; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n; vector<pair<int,int> > ve[maxm]; struct node { int root,tot; int ch[maxn][2]; bool tag[maxn]; void insert(ll L,ll R,ll l,ll r,int &x) { if(!x) x=++tot; if(L<=l&&R>=r) { tag[x]=true; return; } if(L<=mid) insert(L,R,l,mid,ch[x][0]); if(R>mid) insert(L,R,mid+1,r,ch[x][1]); } }A,B; ll dfs(int d,ll v) { for(int i=0;i<ve[d].size();++i) { pair<int,int> t=ve[d][i]; if(A.tag[t.first]||B.tag[t.second]) return (v+((((ll)1<<d)-1)%p)*inv2%p)%p*(((ll)1<<d)%p)%p; } ll val=0; for(int x=0;x<=1;++x) { ve[d-1].clear(); for(int i=0;i<ve[d].size();++i) { pair<int,int> t=ve[d][i]; for(int y=0;y<=1;++y) if(A.ch[t.first][y]&&B.ch[t.second][x^y]) ve[d-1].push_back(mk(A.ch[t.first][y],B.ch[t.second][x^y])); } if(ve[d-1].size()) val=(val+dfs(d-1,v|((ll)x<<(d-1))))%p; } return val; } int main() { read(n); for(int i=1;i<=n;++i) { ll l,r; read(l),read(r); A.insert(l,r,0,((ll)1<<60)-1,A.root); } read(n); for(int i=1;i<=n;++i) { ll l,r; read(l),read(r); B.insert(l,r,0,((ll)1<<60)-1,B.root); } ve[60].push_back(mk(A.root,B.root)),printf("%lld",dfs(60,0)); return 0; }