CF1261F Xor-Set (拆区间、扫描线)
发布日期:2021-06-27 15:38:08 浏览次数:2 分类:技术文章

本文共 5066 字,大约阅读时间需要 16 分钟。

题面

给出两个整数集合 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
#include
#include
#include
#include
#include
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;}

转载地址:https://blog.csdn.net/weixin_43960414/article/details/111657539 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:后缀自动机算法复习回顾
下一篇:Manacher算法讲解——字符串最长回文子串

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月10日 07时42分19秒