CompetitionAD20220211

题面

A.

原题 AGC015D

两个题目实质上是一样的。。。

分析一手这个异或和中每一位的贡献。

发现这一位的数字上只要存在 1,这一位的贡献就是 $2^{n-1}\times 2^p$,其中 $p$ 是这一位的位数。

于是问题就转化成了不多于 $n$ 个数字或在一起能得到的值的种类数。

然后我只会暴力,交完走人。

后来才知道直接撞车,原来 ZR 人均能切 AGCD。。。

其实这个 $n$ 在 $n>2$ 和 $n=2$ 是同一个情况,$n=1$ 的时候特判一下就完了。

为什么 $n>2$ 与 $n=2$ 等价,可以设如果一个数由多个数或起来构成,那必然可以找到一个最高位不同的两个数字,然后我们可以把那位为 0 的数字或上剩下的数字,结果和多个数字还是一样的。

接下来讨论一下具体做法。

显然,对于 $l$ 和 $r$ 这两个数,高位全部相同就完全不用考虑。

我们找到从高到低第一个不同的位置,称之为 $p$。

因为 $r>l$,显然这一位满足 $r$ 在位置 $p$ 上是 1,而 $l$ 在位置 $p$ 上是 0。

接下来就是人类智慧构造。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
这个是 r:
1101... 1 ...1010
相同的高位 p 剩余的低位

这个是 l:
1101... 0 ...0011
相同的高位 p 剩余的低位

构造这个 z:
1101... 1 ...0000
相同的高位 p 剩余的低位全部是 0

然后这个 $z$ 可以用来干什么?

如果我们把 $[l,r]$ 用 $z$ 分成 $[l,z)$ 和 $[z,r]$。

那么 $[z,r]$ 这个区间内相互或可以得到 $[z,q]$ 的所有整数。

1
2
3
这个 q 就是:
1101... 1 00... 111...111
相同的高位 p 全是 0 r 在 p 之后的第一个 1,后面全是 1

这个很容易想。并且 $q$ 是最大值。

同时 $[l,z)$ 可以产生的数显然还是 $[l,z)$。

最后两个区间交叉或的话,显然最小值是 $l\operatorname{or} z$。而最大值就是 $z\operatorname{or}(z-1)$(这个显然大于等于 $r$ 也大于等于 $q$)。而且显然最大值与最小值之间的连续值是都可以取到的。

然后我们最后答案的区间就是 $[l,q]\cup [l\operatorname{or}z,z\operatorname{or}(z-1)]$。

然后我们只要分类一下 $q$ 与 $l\operatorname{or} z$ 的大小关系输出就完了。

时间复杂度 $O(T\log v)$。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;

const ll N=5e2;

ll T,n,l,r;

inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<48||ch>57) {if(ch==45) f=-f;ch=getchar();}
while(ch>=48&&ch<=57) {ret=(ret<<3)+(ret<<1)+ch-48;ch=getchar();}
return ret*f;
}

inline void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {do{buf[++len]=x%10+48;x/=10;}while(x);}
else {putchar(45);do{buf[++len]=-(x%10)+48;x/=10;}while(x);}
while(len>=0) putchar(buf[len--]);
}

inline void writeln(ll x) {write(x);putchar(10);}

int main() {

T=read();

while(T--) {
n=read();l=read();r=read();
if(n==1) {writeln(r-l+1);continue;}
if(l==r) {writeln(1);continue;}
ll pos=0,z=0,ans=0;
for(ll i=62;i>=0;i--) {
if(((l>>i)&1)!=((r>>i)&1)) {z=(r>>i)<<i;pos=i;break;}
}
ll r1=z,l2=l|z,r2=z|(z-1);
for(ll i=pos-1;i>=0;i--) {
if((r>>i)&1) {r1=z|((1ll<<(i+1))-1);break;}
}
if(r1<l2) ans=(r1-l+1)+(r2-l2+1);
else ans=r2-l+1;
writeln(ans);
}

return 0;
}

B.

咕。

C.

咕。