A-cat vj链接
题意:
T次测试
(
T
≤
5
×
1
0
5
)
(T\le5\times10^5)
(T≤5×105),每次给定
L
、
R
L、R
L、R区间以及数字
S
S
S,要求在这个区间中找到最长子段
[
x
,
y
]
[x,y]
[x,y],使得
x
⨁
(
x
+
1
)
⨁
.
.
.
⨁
(
y
−
1
)
⨁
y
≤
S
x\bigoplus (x+1)\bigoplus...\bigoplus(y-1)\bigoplus y\le S
x⨁(x+1)⨁...⨁(y−1)⨁y≤S成立。
数据范围:
1
≤
L
≤
R
≤
1
0
18
,
1
≤
S
≤
2
×
1
0
18
1\le L\le R\le10^{18},1\le S \le 2\times10^{18}
1≤L≤R≤1018,1≤S≤2×1018
思路:
找规律,打表出来的图是这样的。每次固定开头,固定结尾挺难看出来的。注意每次0出现的位置。
这是偶数4开头的数字:
这是偶数2开头的数字:
但是奇数开头没有这个规律。
这是奇数3开头的数字:
可以发现一个规律偶数开头的,区间长度是4的倍数出现一次0
那么问题就可以转换成贪心先把所有偶数开头的,区间长度为4的小区间合并,他们的答案是最小的0。
这样操作之后区间内可能剩下的有两类数字:
- 开头的一个奇数
- 尾部的余数
剩下的数字对于答案的拓展作用只在于:
0
⨁
?
0\bigoplus?
0⨁?之后的答案
≤
S
\le S
≤S,那么最后答案可以加上
?
?
?的长度。
对他们进行前缀和,每次进行枚举前后端点,判断是否是合法的解,维护最大值。
由于这个区间的长度范围
≤
4
\le4
≤4,所以最后的时间复杂度是
O
(
T
∗
16
)
O(T*16)
O(T∗16)。
注意点:
- 因为数据范围是 1 0 18 10^{18} 1018级别的,cin会t,用scanf
- 判断时候异或操作^要加括号!!!!!!!
AC代码:
LL xo(LL x) {
LL tt = x, pos = 1, one, zero;
x++;
LL ans = 0;
while (tt) {
zero = x / (1ll << pos)*(1ll << (pos - 1)) + min(x % (1ll << pos), 1ll << (pos - 1));
one = x - zero;
if (one % 2)ans += 1ll << (pos - 1);
tt >>= 1;
pos++;
}
return ans;
}
LL dp[110], a[110];
int main(){
int t, nw;
LL l, r, s, ans;
l = 0;
/*for (LL i = 0; i <= 10; i++) {
for (LL j = i + 1; j <= 20; j++) {
ans = xo(i) ^ xo(j);
cout << "[" << i + 1 << "," << j << "] " << ans << endl;
}
}*///偶数开头的,区间长度是4的倍数出现一次0
cin >> t;
while (t--) {
nw = 0;
scanf("%lld%lld%lld", &l, &r, &s);
//cin >> l >> r >> s;
ans = 0;
a[0] = 0; dp[0] = 0;
if (l % 2)a[++nw] = l, dp[nw] = dp[nw - 1] ^ a[nw], l++;
ans += (r - l + 1) / 4 * 4;
/*a[++nw] = 0;
dp[nw] = dp[nw - 1] ^ a[nw];*/
if ((r - l + 1) % 4) {
for (LL i = l + (r - l + 1) / 4 * 4; i <= r; i++) {
a[++nw] = i, dp[nw] = dp[nw - 1] ^ a[nw];
}
}
LL tt = ans;
for (int i = 0; i <= nw; i++) {
for (int j = i + 1; j <= nw; j++) {
if ((dp[j] ^ dp[i]) <= s)ans = max(ans, tt + (j - i));
}
}
if (ans)printf("%lld\n", ans);
else printf("-1\n");
/*if (ans)cout << ans << endl;
else cout << -1 << endl;*/
}
return 0;
}