取反(分块+二分)
给一个长度是n的数组,a[1],a[2],a[3],...a[n-1],a[n],初始时a数组中所有的元素都为0,下面有两种操作:
1.指定一个区间[x,y], 把a[x],a[x+1],...a[y]的值取反,即如果a[i]的值为1则把a[i]的值变为0,如果a[i]的值为0则把a[i]的值变为1
2.指定一个区间[x,y], 求a[x],a[x+1],...a[y]中有多少个值为1的元素
输入格式
第一行两个整数n和m,分别表示数组a的长度和操作次数
接下来m行,每行三个整数op,x,y
如果op=0,则此时进行第一种操作
如果op=1,则此时进行第二种操作
2<=n<=1e5,1<=m<=1e5,1<=x,y<=n, 0<=op<=1
输出格式
对于每次的第二种操作,输出一个整数,表示区间中值是1的个数
输入/输出例子1
输入:
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
输出:
1
2
样例解释
无
原题:https://www.luogu.com.cn/problem/P3870
比较恶心,总体还是可以用分块思想
操作1
中间段开数组标记(注意,如果被改2次,相当于没改,取反即可0变1,1变0),两边直接改(改的时候也要看看标记数组),然后开一个数组维护区间和
void change(int L, int R)
{
int p=pos[L], q=pos[R];
if (p==q)
{
for (int i=L; i<=R; i++)
{
if (a[i]==1) a[i]=0, sum[p]--; //改0,数量减一
else a[i]=1, sum[p]++; //改1,数量加一
}
}
else
{
for (int i=p+1; i<=q-1; i++) add[i]^=1; //标记数组
for (int i=L; i<=ed[p]; i++)
{
if (a[i]==1) a[i]=0, sum[p]--;
else a[i]=1, sum[p]++;
}
for (int i=st[q]; i<=R; i++)
{
if (a[i]==1) a[i]=0, sum[q]--;
else a[i]=1, sum[q]++;
}
}
}
操作2
两边段有4种情况
1.标记数组=1,本身数字=0(此时要转换成开灯,答案肯定是累加的)
2.标记数组=1,本身数字=1(此时要转换成关灯,答案肯定无用)
3.标记数组=0,本身数字=1(此时就是开灯,答案肯定是累加的)
4.标记数组=0,本身数字=0(此时就是关灯,答案肯定无用)
中间段直接看标记,如果是1,就是区间关灯数量(取反),如果是0,就是开灯数量(取反)
区间关灯数量=区间长度减去当前区间开灯数
int query(int L, int R)
{
int p=pos[L], q=pos[R], ans=0, tmp=0;
if (p==q)
{
for (int i=L; i<=R; i++)
if ((a[i]==1 && add[p]==0) || (a[i]==0 && add[p]==1)) ans++;
}
else
{
for (int i=p+1; i<=q-1; i++)
{
if (add[i]) ans+=(ed[i]-st[i]+1)-sum[i];
else ans+=sum[i];
}
for (int i=L; i<=ed[p]; i++)
if ((a[i]==1 && add[p]==0) || (a[i]==0 && add[p]==1)) ans++;
for (int i=st[q]; i<=R; i++)
if ((a[i]==1 && add[q]==0) || (a[i]==0 && add[q]==1)) ans++;
}
return ans;
}
、
另外,每次查询完,标记数组不能清零,毕竟可能是要查询的区间是一小段,但这一段的其他元素也要用到这个标记数组,只要维护了sum,就可以了
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n, m, op, x, y, a[N], sum[N], add[N], pos[N], st[N], ed[N];
void init()
{
int block=sqrt(n);
int t=n/block;
if (n%block) t++;
for (int i=1; i<=t; i++)
{
st[i]=(i-1)*block+1;
ed[i]=i*block;
}
ed[t]=n;
for (int i=1; i<=n; i++) pos[i]=(i-1)/block+1;
}
void change(int L, int R)
{
int p=pos[L], q=pos[R];
if (p==q)
{
for (int i=L; i<=R; i++)
{
if (a[i]==1) a[i]=0, sum[p]--;
else a[i]=1, sum[p]++;
}
}
else
{
for (int i=p+1; i<=q-1; i++) add[i]^=1;
for (int i=L; i<=ed[p]; i++)
{
if (a[i]==1) a[i]=0, sum[p]--;
else a[i]=1, sum[p]++;
}
for (int i=st[q]; i<=R; i++)
{
if (a[i]==1) a[i]=0, sum[q]--;
else a[i]=1, sum[q]++;
}
}
}
int query(int L, int R)
{
int p=pos[L], q=pos[R], ans=0, tmp=0;
if (p==q)
{
for (int i=L; i<=R; i++)
if ((a[i]==1 && add[p]==0) || (a[i]==0 && add[p]==1)) ans++;
}
else
{
for (int i=p+1; i<=q-1; i++)
{
if (add[i]) ans+=(ed[i]-st[i]+1)-sum[i];
else ans+=sum[i];
}
for (int i=L; i<=ed[p]; i++)
if ((a[i]==1 && add[p]==0) || (a[i]==0 && add[p]==1)) ans++;
for (int i=st[q]; i<=R; i++)
if ((a[i]==1 && add[q]==0) || (a[i]==0 && add[q]==1)) ans++;
}
return ans;
}
signed main()
{
scanf("%d%d", &n, &m);
init();
while (m--)
{
scanf("%d%d%d", &op, &x, &y);
if (op==0)
{
change(x, y);
}
else if (op==1)
{
printf("%lld\n", query(x, y));
}
}
return 0;
}

浙公网安备 33010602011771号