CF1261F Xor-Set (拆区间、扫描线)

题面

给出两个整数集合 A , B A,B A,B
求所有使得存在 a ∈ A , b ∈ B a∈A,b∈B aA,bB a ⊕ b = c a⊕b=c ab=c 的整数 c c c 之和取模 998244353 998244353 998244353的值(所以说如果有多个 a ⊕ b a⊕b ab 等于同一个 c c c ,则该 c c c 只计算一次贡献),
其中 ⊕ ⊕ 表示按位异或,即 x o r xor xor

对于 A A A ,给出 n A n_A nA ​,
然后给出 n A n_A nA ​个区间 [ l i , r i ] [l_i,r_i] [li,ri] ,表示对于所有满足 l i ≤ a ≤ r i l_i \le a \le r_i liari​ 的整数 a a a 都有 a ∈ A a \in A aA (即 [ l i , r i ] ∈ A [l_i,r_i] \in A [li,ri]A )。

对于 B B B 以同样方式给出其中的元素。

A , B A,B A,B 都是不重集,即每个整数至多在集合中出现一次(虽然对题意没有影响)。数据范围: 1 ≤ n A , n B ≤ 100 , 1 ≤ l i , r i ≤ 1 0 18 1 \le n_A,n_B \le 100,1 \le l_i,r_i \le 10^{18} 1nA,nB100,1li,ri1018

题解

这题好难啊做不来只好看题解了

首先肯定不能暴力两两异或,题解都是这么说的。

然后,可不可以区间与区间计算呢?可它是异或的嘛😕……

不过,我们发现还是有一些特殊的区间是可以两两计算的,

比如,形如 [ L   ,   L + 2 k ) [L\,,\,L+2^k) [L,L+2k)(即 [ L   ,   L + 2 k − 1 ] [L\,,\,L+2^k-1] [L,L+2k1]) 的区间,其中满足 L    &    ( 2 k − 1 ) = = 0 L\;\&\;(2^k-1)==0 L&(2k1)==0,或者也可以这样表示:
{ x    ∣    x   &   ( 11...1111   00...0 ) 2 ( k 个 0 ) = = L , L    &    ( 2 k − 1 ) = = 0 } \{ x \;|\; x \,\&\, (11...1111\,00...0)_2(k个0) == L, L\;\&\;(2^k-1)==0 \} {xx&(11...111100...0)2(k0)==L,L&(2k1)==0}
(即前面固定,后面一段k位任意)

这样的两个区间计算结果怎样呢?

我们不妨设 [ L A , R A ] [L_A,R_A] [LA,RA] 长度更小,然后我们只讨论 60 个二进制位
在这里插入图片描述
把它们异或起来,前面的 l e n B len_B lenB 位异或起来肯定是一个固定的值,
后面的 60 − l e n B 60-len_B 60lenB 位,由于 [ L B , R B ] [L_B,R_B] [LB,RB] 在这些位置是任意的(每一位不论取0或1最终的数都在这个区间中),因此这些位置异或起来的答案也是任意的。
在这里插入图片描述
它们于是得到了一个连续的区间!!

所以我们考虑把 A A A 的所有区间和 B B B 的所有区间都拆成这样的特殊的区间,就可以计算了。

对于一个区间 [ L , R ) [L,R) [L,R) ,我们可以把它拆成前面 l o g log log 个区间:(注意开闭)
[ L , L + l o w b i t ( L )    )    , [ L + l o w b i t ( L ) , L + l o w b i t ( L ) + l o w b i t ( L + l o w b i t ( L ) )    )    , . . . . . . [L,L+lowbit(L)\;)\;,\\ [L+lowbit(L),L+lowbit(L) + lowbit(L+lowbit(L))\;)\;,\\ ...... [L,L+lowbit(L)),[L+lowbit(L),L+lowbit(L)+lowbit(L+lowbit(L))),......
加上后面 l o g log log 个区间:
. . . . . . [ R − l o w b i t ( R ) − l o w b i t ( R − l o w b i t ( R ) ) , R − l o w b i t ( R )    ) [ R − l o w b i t ( R ) , R    ) ......\\ [R-lowbit(R)-lowbit(R-lowbit(R)),R-lowbit(R)\;)\\ [R-lowbit(R),R\;) ......[Rlowbit(R)lowbit(Rlowbit(R)),Rlowbit(R))[Rlowbit(R),R)

通过学习树状数组的经验,我们可以知道 L L L 一直加 l o w b i t ( L ) lowbit(L) lowbit(L) 、和 R R R 一直减 l o w b i t ( R ) lowbit(R) lowbit(R) 最终一定会相遇于一个数,所以证明了这 2 l o g 2log 2log 个区间并起来是连续的、且恰好等于原区间 [ L , R ) [L,R) [L,R).

我们再看一下加lowbit的过程:
加lowbit的动态过程在这里插入图片描述
可以发现它开头的一段都没变,最多尾端的一个 0 变成了 1 ,

所以当B的一个新区间和A的一个原始区间 [ L , R ) [L,R) [L,R) 计算时,
只需要分别和 [ L , L + l o w b i t ( L )    ) [L,L+lowbit(L)\;) [L,L+lowbit(L)) 以及 [ R − l o w b i t ( R ) , R    ) [R-lowbit(R),R\;) [Rlowbit(R),R)计算,如果有的话,最多再加上一个和它一样长的区间。

然后再把 A,B 调转顺序跑一遍大的。

这样最终就只有 n A  ⁣ ⁣ ⋅  ⁣ n B  ⁣ ⁣ ⋅  ⁣ l o g n_A\!\!\cdot \!n_B\!\!\cdot \!log nAnBlog 个区间,
排个序,用扫描线,
按左端点排序,从左边扫,依次合区间(区间并集),
若发现再合并上当前区间时会变得不连续,那么就把原先的区间的贡献计算了,然后变成空集和它合并,这应该很好理解吧 🙂

CODE

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring> 
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 205
#define MAXM 4000005
#define LL long long
#define DB double
#define ENDL putchar('\n') 
#define MM(x) memset((x),0,sizeof((x)))
#define MAXI(x,y) ((x) = max((x),(y)))
#define MINI(x,y) ((x) = min((x),(y)))
#define lowbit(x) (-(x) & (x))
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return x * f;
}
const int MOD = 998244353;
const int inv2 = (MOD+1) / 2;
int n,m,i,j,s,o,k,cnc;
struct it{
	LL l,r;
	it(){l=r=0;}
	it(LL L,LL R){l=L;r=R;}
}A[MAXN],B[MAXN],a[MAXN<<1],b[MAXN<<1],c[MAXM];
bool cmp(it a,it b) {return a.l < b.l;}
void spli(it a,it *b,int &n) {
	n = 0;	
	LL ll = a.l,rr = a.r+1;
	for(LL i = ll;i+lowbit(i) <= rr;i += lowbit(i)) 
		b[++ n] = it(i,i+lowbit(i));
	for(LL i = rr;i-lowbit(i) >= ll;i -= lowbit(i)) 
		b[++ n] = it(i-lowbit(i),i);
	return sort(b + 1,b + 1 + n,cmp);
}
it merg(it a,it b) {
	LL x = max(a.r-a.l-1,b.r-b.l-1);
	it c; c.l = a.l ^ b.l; c.l = c.l - (c.l&x);
	c.r = c.l + x; return c;
}
int solve(LL l,LL r) {
	return (l + r) % MOD *1ll* ((r - l + 1) % MOD) % MOD *1ll* inv2 % MOD;
}
int main() {
	n = read();
	for(int i = 1;i <= n;i ++) {
		LL s = read(),o = read();A[i] = it(s,o);
	}
	m = read();
	for(int i = 1;i <= m;i ++) {
		LL s = read(),o = read();B[i] = it(s,o);
	}
	for(int i = 1;i <= n;i ++) {
		int la,lb;
		spli(A[i],a,la);
		for(int j = 1;j <= m;j ++) {
			spli(B[j],b,lb);
			for(int k = 1;k <= la;k ++) {
				if(a[k].r-a[k].l > b[1].r-b[1].l) c[++ cnc] = merg(a[k],b[1]);
				if(a[k].r-a[k].l > b[lb].r-b[lb].l) c[++ cnc] = merg(a[k],b[lb]);
				for(int l = 1;l <= lb;l ++) {
					if(a[k].r - a[k].l == b[l].r - b[l].l) {
						c[++ cnc] = merg(a[k],b[l]);
					}
				}
			}
			for(int k = 1;k <= lb;k ++) {
				if(b[k].r-b[k].l > a[1].r-a[1].l) c[++ cnc] = merg(b[k],a[1]);
				if(b[k].r-b[k].l > a[la].r-a[la].l) c[++ cnc] = merg(b[k],a[la]);
			}
		}
	}
	sort(c + 1,c + 1 + cnc,cmp);
	int ans = 0;
	LL ll = 0,rr = -1;
	for(int i = 1;i <= cnc;i ++) {
		if(c[i].l > rr) {
			(ans += solve(ll,rr)) %= MOD;
			ll = c[i].l; rr = c[i].r;
		}
		else rr = max(rr,c[i].r);
//		printf("[%lld,%lld]\n",c[i].l,c[i].r);
	}
	(ans += solve(ll,rr)) %= MOD;
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值