CF1261F Xor-Set
题意:
题解:由于肯定不能一个一个去进行异或,考虑两堆数异或会有什么性质。
发现这样一个性质:如果两堆数的长度都是\(2^l\),且两堆数的高位分别相同,比如说{\(0,1,2,3\)}和{\(4,5,6,7\)},那么它们做异或操作得到的依然是这样的一个序列。仔细观察,这两个数列中的数的二进制的前\(l\)位刚好组成一个全排列,而后面的位数上的数都是相同的。那么,它们异或后得到的序列也一定是前\(l\)位是全排列,后面的位都一样。对于两个长度都为2的次方,但长度不同的序列,也有相似的性质。
所以能想到的一个做法就是找到这些长度刚好是2的次方的序列,一一进行操作。考虑到线段树一分为二的性质,我们可以开一棵值域为\([0,2^{60}-1]\)的线段树,这样一条线段就会被分成log个我们想要的区间。
但这样做的话,空间复杂度会达到\(O(n^2log^21e18)\),因为要存储所有得到的线段。这样是会MLE的。考虑优化。
考虑空间浪费的主要原因是要两两进行异或,而且两部分的长度不同,但得到的线段的长度却是较长的那个的长度。那么考虑能否将所有长度不等两序列的异或转化为长度相等的两序列的异或。发现对于两个序列,长度分别为\(2^A\)个\(2^B\),令\(A>B\),那么由于得到的序列的前\(A\)位是全排列,我们不妨将长度较小的序列补成长度为\(2^A\)的序列。这样的操作其实就相当于在线段树上跳father。所以对于原来的\(a,b\)序列,我们可以将\(a\)像正常的动态开点线段树区间修改那样,将线段挂在终止节点上,对于\(b\)序列,将其访问到的所有区间都打上标记。然后我们再枚举深度,分别进行计算。这样,我们就能算出每个\(a\)对所有\(b\)的贡献。算完后,再按照上面的过程,将\(a,b\)序列交换一下,再做一遍就行了。这样,空间复杂度就是\(O(n^2log1e18)\)了,可以接受。令\(T=n^2log1e18\),那么时间复杂度就是\(O(T)\)或者\(O(TlogT)\),取决于你用什么排序算法。
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register ll
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline ll
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
const int Mod=998244353,inv2=499122177;
const ll N=(1ll<<60)-1;
struct P{
ll x,y;
P(ll _x=0,ll _y=0){x=_x;y=_y;}
friend bool operator < (P a,P b){
return a.x==b.x?a.y<b.y:a.x<b.x;
}
}a[110],b[110];
ll n,m;
ll cnt,root,lc[404000],rc[404000],w[404000],L,R,ans;
vector<P>veca[70],vecb[70],vecc;
I add(ll &x,ll y){(x+=y)>=Mod?x-=Mod:0;}
IN Plus(ll x,ll y){(x+=y)>=Mod?x-=Mod:0;return x;}
I modi(ll &k,ll l,ll r,ll x,ll y){
if(x>r||y<l)return;
if(!k)k=++cnt,lc[k]=rc[k]=w[k]=0;
if(x<=l&&r<=y)return w[k]=1,void();
re mid=(l+r)>>1;
modi(lc[k],l,mid,x,y),modi(rc[k],mid+1,r,x,y);
}
I revi(ll l,ll r,ll x,ll y,ll dep){
if(x>r||y<l)return;
vecb[dep].emplace_back(P(l,r));
if(x<=l&&r<=y)return;
re mid=(l+r)>>1;
revi(l,mid,x,y,dep+1);revi(mid+1,r,x,y,dep+1);
}
I getit(ll k,ll l,ll r,ll dep){
if(!k)return;
if(w[k])veca[dep].emplace_back(P(l,r));
re mid=(l+r)>>1;
getit(lc[k],l,mid,dep+1);getit(rc[k],mid+1,r,dep+1);
}
I solve(){
re l,r,w;
F(i,1,60)for(auto x:veca[i])for(auto y:vecb[i]){
w=(x.x^y.x);w>>=(60-i);w<<=(60-i);l=w;r=l+(1ll<<(60-i))-1ll;
vecc.emplace_back(P(l,r));
// cout<<"!"<<l<<" "<<r<<endl;
}
}
IN calc(ll x,ll y){
x%=Mod;y%=Mod;
return (ll)Plus(y+1,Mod-x)*Plus(x,y)%Mod*inv2%Mod;
}
int main(){
read(n);
F(i,1,n)read(a[i].x),read(a[i].y);
sort(a+1,a+1+n);
read(m);
F(i,1,m)read(b[i].x),read(b[i].y);
sort(b+1,b+1+m);
F(i,1,n)modi(root,0,N,a[i].x,a[i].y);
F(i,1,m)revi(0,N,b[i].x,b[i].y,0);
getit(root,0,N,0);
solve();
F(i,1,60)veca[i].clear(),vecb[i].clear();root=cnt=0;
F(i,1,n)revi(0,N,a[i].x,a[i].y,0);
F(i,1,m)modi(root,0,N,b[i].x,b[i].y);
getit(root,0,N,0);
solve();
F(i,1,60)veca[i].clear(),vecb[i].clear();
sort(vecc.begin(),vecc.end());
if(vecc.begin()==vecc.end())return !printf("0");
L=vecc[0].x;R=vecc[0].y;
for(auto d:vecc){
if(d.x<=R)R=max(R,d.y);
else add(ans,calc(L,R)),L=d.x,R=d.y;
}
add(ans,calc(L,R));
cout<<ans;
return 0;
}