将区间拆分为 \([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; }