题解:CF1732C1 Sheikh (Easy version)

文章讲述了CF1732C1的一个编程问题,涉及计算区间内数的和与异或差值的求解策略,利用单调性进行二分搜索和双指针技巧,给出了详细的解题思路和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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,jxori,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+1xori,j+1sumi,j+xori,j

最后得出等于 arj+1−xori,j+1+xori,jar_{j + 1} - xor_{i, j + 1} + xor_{i, j}arj+1xori,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)<resrrr 没有超过 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;
}

这样这道题就完成啦!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值