首页 > 其他分享 >取反(分块+二分)

取反(分块+二分)

时间:2024-03-12 15:59:35浏览次数:21  
标签:二分 标记 int 分块 取反 pos 数组 区间

第2题     取反 查看测评数据信息

给一个长度是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;
}

  

 

标签:二分,标记,int,分块,取反,pos,数组,区间
From: https://www.cnblogs.com/didiao233/p/18068474

相关文章

  • 统计数量(分块+二分)
    第1题   统计数量 查看测评数据信息给一个长度是n的正整数数组,a[1],a[2],a[3],...a[n-1],a[n],其中1<=a[i]<=1000。现在在数组a上进行m次操作:1.Mxyz,表示对a数组的闭区间[x,y]内所有a[i]的值分别加上z2.Axyzz,询问a数组闭区间[x,y]内有多少a[i]的值大于等于z......
  • 分块
    与线段树类似。是暴力的优化版,详细见下两个链接https://www.cnblogs.com/milky-w/p/8447724.htmlhttps://www.cnblogs.com/Miracevin/p/9403620.html  板子:#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e5+5;intn,m,a[N],s......
  • POJ--3258 River Hopscotch(二分搜索/最大化最小值)
    记录10:232023-3-11http://poj.org/problem?id=3259二分法查找最大的可能解,检查x是否符合条件(当前这个位置上的值-前上一个选取位置的值>=x)注意的点:使用了[begin,end)的左闭右开区间,所以结果要begin-1,end要从L+1开始算点击查看代码intL,N,M;introcks[5......
  • 蓝桥杯算法集训 - Week1:二分、前缀和、差分算法
    蓝桥杯算法集训-Week1本系列随笔用于整理AcWing题单——《蓝桥杯集训·每日一题2024》的系列题型及其对应的算法模板。一、二分查找二分算法原理复习参考:二分查找-Hello算法Ⅰ、二分模板boolcheck(intx){/*...*/}//检查x是否满足某种性质//区间[l,r]被划分......
  • 1005. K 次取反后最大化的数组和c
    intlargestSumAfterKNegations(int*nums,intnumsSize,intk){intt[201]={0};intsum=0;for(inti=0;i<numsSize;i++){t[100+nums[i]]++;sum+=nums[i];}while(k>0){for(inti=0;i<201;i++){......
  • 第一节二分
    第一节:二分定义:(一遍恒比这个值小一遍恒比这个值大为了找出这个值第一次或最后一次出现的位置所以用二分)注意,这里的有序是广义的有序,如果一个数组中的左侧或者右侧都满足某一种条件,而另一侧都不满足这种条件,也可以看作是一种有序(如果把满足条件看做 ,不满足看做 ,至少对于这个条......
  • 分块小结
    分块概念就是把一个长序列分成\(\sqrt{n}\)个区间,分别维护每个区间内的信息和,然后查询时可以优化时间复杂度。还可以完成一些线段树完成不了的神秘操作,比如这道题。但是总体时间复杂度不如线段树,但它的扩展性比线段树还要强,因为分块中每个区间的信息和不需要具有传递性。怎......
  • 二分搜索模板
    目录最基本的二分搜索寻找左边界的二分搜索寻找右边界的二分搜索统一记忆为闭区间,只需要修改nums[mid]==target时收缩哪边边界,以及越界情况最基本的二分搜索defbinary_search(nums:List[int],target:int):left=0right=len(nums)-1while(left<=right):......
  • 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。
    704.二分查找https://leetcode.cn/problems/binary-search/description/一、左闭右闭`//左闭右闭publicstaticinterFen1(int[]nums,inttarget){if(target<nums[0]||target>nums[nums.length-1]){return-1;}intmaxIndex=nums.length-......
  • 莫队与分块学习笔记
    分块思想介绍分块是一种思想,而不是一种数据结构。思想就是,将一块大的区间,转换成小的区间来处理。例如,在一个\(n\)长度上的数轴,我们可以将其分成\(\sqrtn\)个长度为\(\sqrtn\)的块来解决。典型问题对于一类很典型的问题,可以用分块来做。单点修改,区间查询这玩意咋......