1. 程式人生 > 實用技巧 >CF1261F Xor-Set

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;
}