【Codeforces】Codeforces Round #830 (Div. 2) C1+C2. Sheikh | 二进制

62 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

【Codeforces】Codeforces Round #830 (Div. 2) C1+C2. Sheikh | 二进制

题目链接

Sheikh (Easy version)

Sheikh (Hard Version)

题目

image.png

题目大意

给定长度为 nn 的数组 a1,a2,...,ana_1,a_2,...,a_n,有 qq 个询问,每个询问给出 1LRn1\le L\le R\le n,试求在区间 aL,aL+1,...,aR1,aRa_L,a_{L+1},...,a_{R-1},a_R 的所有子区间中哪个子区间里所有元素之和减去所有元素的异或和之差最大,输出所有满足条件的最短的子区间的左右下标。

思路

显然 f(l,r)f(l,r+1)f(l, r) \leq f(l, r + 1)。证明如下:
sum(l,r)sum(l,r) 表示区间 al,al+1,...,ara_l,a_{l+1},...,a_r 中所有元素之和,xorsum(l,r)xorsum(l,r) 表示所有元素之异或和,f(l,r)=sum(l,r)xorsum(l,r)f(l,r)=sum(l,r)-xorsum(l,r)。当我们把 ar+1a_{r+1} 加入当前区间, sum(l,r+1)=sum(l,r)+ar+1sum(l,r+1)=sum(l,r)+a_{r+1},但是 xorsum(l,r+1)xorsum(l,r)+ar+1xorsum(l,r+1)\le xorsum(l,r)+a_{r+1}。由此我们可以得知 ff 的最大值可以在整个区间 [L,R][L,R] 上取得。

接下来讨论在 xorsum(l,r)xorsum(l,r)ar+1a_{r+1} 不同的取值对 f(l,r+1)f(l,r+1) 造成的影响:

  • 如果 xorsum(l,r)&ar+1=0xorsum(l,r)\& a_{r+1}=0,则 f(l,r+1)=f(l,r)f(l,r+1)=f(l,r)
  • 否则 f(l,r)<f(l,r+1)f(l,r)< f(l,r+1)

这说明最终答案的长度最小不会太小,因为原数组中元素大小不超过 1e9,所以从 aL,aL+1,...,aRa_L,a_{L+1},...,a_R 中去除 31 个非 0 元素,剩下区间的 ff 值就一定会小于 f(L,R)f(L,R)

我们先预处理出每个元素前后第一个非零元素的位置,再枚举从区间 [L,R][L,R] 的左段删除元素的数量,求出使子区间 ff 值等于 f(L,R)f(L,R) 的最小右端点。取最小值记录答案输出。

代码

#include <bits/stdc++.h>
using namespace std;
using LL=long long;
const int N=500001;
//const LL mod=1000000007;
const LL mod=998244353;
int n,m,k;
int a[N];
int sum[N],xorsum[N];
int pre[N],nxt[N];
int check(int l1,int r1,int l2,int r2)
{
	if ((sum[r1]-sum[l1-1])-(xorsum[r1]^xorsum[l1-1])==(sum[r2]-sum[l2-1])-(xorsum[r2]^xorsum[l2-1])) return 1;
	return 0;
}
int solve()
{
	for (int i=1;i<=n;++i) sum[i]=xorsum[i]=0;
	scanf("%d%d",&n,&k);
	for (int i=1,t=0;i<=n;++i)
	{
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
		xorsum[i]=xorsum[i-1]^a[i];
		pre[i]=t;
		if (a[i]!=0) t=i;
	}
	for (int j=n,t=n+1;j>=1;--j)
	{
		nxt[j]=t;
		if (a[j]) t=j;
	}
	for (int L,R,x,y,mid,l,r;k--;)
	{
		scanf("%d%d",&L,&R);
		for (r=R;L<=r-1&&check(L,r-1,L,R);r=pre[r]);
		if (r<L)
		{
			printf("%d %d\n",L,L);
			continue;
		}
		x=L;
		y=r;
		for (l=L+1;l<=R&&check(l,R,L,R);l=nxt[l])
		{
			r=max(l,r);
			while (!check(l,r,L,R)) r=nxt[r];
			if (r-l<y-x)
			{
				x=l;
				y=r;
			}
		}
		printf("%d %d\n",x,y);
	}
	
	return 1;
}
int main()
{
	int T=1;
	scanf("%d",&T);
	if (T==10000) m=1;
	for (;T--;) solve();
	return 0;
}