持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
【Codeforces】Codeforces Round #830 (Div. 2) C1+C2. Sheikh | 二进制
题目链接
题目
题目大意
给定长度为 的数组 ,有 个询问,每个询问给出 ,试求在区间 的所有子区间中哪个子区间里所有元素之和减去所有元素的异或和之差最大,输出所有满足条件的最短的子区间的左右下标。
思路
显然 。证明如下:
设 表示区间 中所有元素之和, 表示所有元素之异或和,。当我们把 加入当前区间, ,但是 。由此我们可以得知 的最大值可以在整个区间 上取得。
接下来讨论在 和 不同的取值对 造成的影响:
- 如果 ,则
- 否则
这说明最终答案的长度最小不会太小,因为原数组中元素大小不超过 1e9,所以从 中去除 31 个非 0 元素,剩下区间的 值就一定会小于 。
我们先预处理出每个元素前后第一个非零元素的位置,再枚举从区间 的左段删除元素的数量,求出使子区间 值等于 的最小右端点。取最小值记录答案输出。
代码
#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;
}