CF1732C1 Sheikh (Easy version) 题解
大致意思:
qqq 次询问,每次求 lll ~ rrr 的区间和减去区间异或值得到差的最大值,若有多个最大值,去取区间长度最短的,若仍然有多个区间,输出其中一个即可,保证 q=1,L=1,R=nq = 1, L = 1, R = nq=1,L=1,R=n。
大致思路:
算出 sum[i]sum[i]sum[i] 为前 iii 个数之和, xor[i]xor[i]xor[i] 为前 iii 个数的异或值。
- 二分,不详讲,大致就是根据定义 f(i,j)=sumi,j−xori,jf(i, j) = sum_{i,j} - xor_{i,j}f(i,j)=sumi,j−xori,j,发现对于区间 [l,r][l,r][l,r] ,f(l,r+1)≥f(l,r)f(l,r+1) ≥ f(l,r)f(l,r+1)≥f(l,r)。
证明:
感谢一楼大佬的证明,让我对这道题更加印象深刻,更加理解。
f(i,j+1)−f(i,j)f(i, j + 1) - f(i, j)f(i,j+1)−f(i,j)
=sumi,j+1−xori,j+1−sumi,j+xori,j= sum_{i, j + 1} - xor_{i, j + 1} - sum_{i, j} + xor_{i, j}=sumi,j+1−xori,j+1−sumi,j+xori,j
最后得出等于 arj+1−xori,j+1+xori,jar_{j + 1} - xor_{i, j + 1} + xor_{i, j}arj+1−xori,j+1+xori,j,证明出 f(i,j+1)−f(i,j)≥0f(i, j + 1) - f(i, j) ≥ 0f(i,j+1)−f(i,j)≥0。
这说明了当左端点固定时,右端点向右移动时, f(i,j)f(i, j)f(i,j) 是单调不减的,具有单调性,显然二分。
代码实现:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-'){
w = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
int n, q, ar[N], sum[N], g[N];//sum:前缀和 g:前缀异或
int get_sum(int l, int r)
{
return sum[r] - sum[l - 1] - (g[r] ^ g[l - 1]);//计算差值
}
signed main()
{
int _;
_ = read();
while (_ --)
{
n = read(), q = read();for(int i = 1;i <= n; ++ i) ar[i] = read(), sum[i] = sum[i - 1] + ar[i], g[i] = g[i - 1] ^ ar[i];
int l = read(), r = read();
int res = get_sum(l, r);
int ll = l, rr = r;
for(int i = 1;i <= r; ++ i)//枚举左端点
{
int Left = i, Right = r, now = 0;
while(Left <= Right)
{
int mid = (Left + Right) >> 1;
if(get_sum(i, mid) != res) Left = mid + 1;
else Right = mid - 1, now = mid;
}
if(now > 0 && rr - ll + 1 > now - i + 1) ll = i, rr = now;
}
cout << ll << " " << rr << "\n";
}
return 0;
}
- 双指针,定义 get_sum(l,r)get\_sum(l, r)get_sum(l,r) 为 lll ~ rrr 的区间和异或之差,我们可以使用尺取法, l,rl, rl,r 指针开始均为 111 ,枚举 lll ,在定义一个 resresres 存最大的 get_sum(n,n)get\_sum(n, n)get_sum(n,n) ,如果 get_sum(l,r)<resget\_sum(l, r) < resget_sum(l,r)<res 且 rrr 没有超过 nnn,那么明显 rrr 可以往后移动,最后再看看当前选择区间是否合法,合法即为目前最优区间。
代码实现:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-'){
w = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
int n, q, ar[N], sum[N], g[N];//sum:前缀和 g:前缀异或
int get_sum(int l, int r)
{
return sum[r] - sum[l - 1] - (g[r] ^ g[l - 1]);//计算差值
}
signed main()
{
int _;
_ = read();
while (_ --)
{
n = read(), q = read();for(int i = 1;i <= n; ++ i) ar[i] = read(), sum[i] = sum[i - 1] + ar[i], g[i] = g[i - 1] ^ ar[i];
int l = 1, r = 1;
int res = sum[n] - g[n];
int ll = read(), rr = read();
for(int i = 1;i <= n; ++ i)//枚举左端点
{
if(r < i) r = i;
while(r <= n && get_sum(i, r) < res)
{
++ r;
}
if(r > n) break;
if(rr - ll + 1 > r - i + 1) ll = i, rr = r;
}
cout << ll << " " << rr << "\n";
}
return 0;
}
这样这道题就完成啦!!!