打卡信奥刷题(2619)用C++实现信奥题 P2574 XOR的艺术

P2574 XOR的艺术

题目描述

AKN 觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏。在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下:

  1. 拥有一个伤害串,是一个长度为 nnn 的只含字符 0 和字符 1 的字符串。规定这个字符串的首字符是第一个字符,即下标从 111 开始。
  2. 给定一个范围 [l, r][l,~r][l, r],伤害为伤害串的这个范围内中字符 1 的个数。
  3. 会修改伤害串中的数值,修改的方法是把 [l, r][l,~r][l, r] 中所有原来的字符 0 变成 1,将 1 变成 0

AKN 想知道一些时刻的伤害,请你帮助他求出这个伤害。

输入格式

输入的第一行有两个用空格隔开的整数,分别表示伤害串的长度 nnn,和操作的个数 mmm

输入第二行是一个长度为 nnn 的字符串 SSS,代表伤害串。

333 到第 (m+2)(m + 2)(m+2) 行,每行有三个用空格隔开的整数 op,l,rop, l, rop,l,r。代表第 iii 次操作的方式和区间,规则是:

  • op=0op = 0op=0,则表示将伤害串的 [l, r][l,~r][l, r] 区间内的 0 变成 11 变成 0
  • op=1op = 1op=1,则表示询问伤害串的 [l, r][l,~r][l, r] 区间内有多少个字符 1

输出格式

对于每次询问,输出一行一个整数,代表区间内 1 的个数。

输入输出样例 #1

输入 #1

10 6
1011101001
0 2 4
1 1 5
0 3 7
1 1 10
0 1 4
1 2 6

输出 #1

3
6
1

说明/提示

样例输入输出 111 解释

原伤害串为 1011101001

对于第一次操作,改变 [2, 4][2,~4][2, 4] 的字符,伤害串变为 1100101001

对于第二次操作,查询 [1, 5][1,~5][1, 5]1 的个数,共有 333 个。

对于第三次操作,改变 [3, 7][3,~7][3, 7] 的字符,伤害串变为 1111010001

对于第四次操作,查询 [1, 10][1,~10][1, 10]1 的个数,共有 666 个。

对于第五次操作,改变 [1, 4][1,~4][1, 4] 的字符,伤害串变为 0000010001

对于第六次操作,查询 [2, 6][2,~6][2, 6]1 的个数,共有 111 个。

数据范围与约定

对于 10%10\%10% 的数据,保证 n,m≤10n, m \leq 10n,m10

另有 30%30\%30% 的数据,保证 n,m≤2×103n, m \leq 2 \times 10^3n,m2×103

对于 100%100\%100% 的数据,保证 2≤n,m≤2×1052 \leq n, m \leq 2 \times 10^52n,m2×1050≤op≤10 \leq op \leq 10op11≤l≤r≤n1 \leq l \leq r \leq n1lrnSSS 中只含字符 0 和字符 1

C++实现

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,h,blk[N],fst[N],lst[N],tag[N],a[N],sum[N][2];
void read(int &x) {scanf("%d",&x);}
void write(int x) {printf("%d\n",x);}
void update(int l,int r){
	int bl=blk[l],br=blk[r];
	if (bl==br) 	{
		for (int i=l;i<=r;i++) sum[bl][a[i]]--,a[i]^=1,sum[bl][a[i]]++;
		return;
	}
	for (int i=bl+1;i<br;i++) tag[i]^=1;
	for (int i=l;i<=lst[bl];i++) sum[bl][a[i]]--,a[i]^=1,sum[bl][a[i]]++;
	for (int i=fst[br];i<=r;i++) sum[br][a[i]]--,a[i]^=1,sum[br][a[i]]++;
}
int query(int l,int r){
	int bl=blk[l],br=blk[r],ans=0;
	if (bl==br) 	{
		for (int i=l;i<=r;i++) if (a[i]^tag[bl]==1) ans++;
		return ans;
	}
	for (int i=bl+1;i<br;i++) ans+=sum[i][1^tag[i]];
	for (int i=l;i<=lst[bl];i++) if (a[i]^tag[bl]==1) ans++;
	for (int i=fst[br];i<=r;i++) if (a[i]^tag[br]==1) ans++;
	return ans;
}
int main(){
	read(n);read(m);
	h=(int)sqrt(n);
	for (int i=1;i<=n;i++) blk[i]=(i-1)/h+1;
	for (int i=1;i<=n;i++)
		if (blk[i]!=blk[i-1]) fst[blk[i]]=i,lst[blk[i-1]]=i-1;
	lst[blk[n]]=n;
	for (int i=1;i<=n;i++)	{
		char s=getchar();
		while (s!='0'&&s!='1') s=getchar();
		a[i]=s-'0';
		sum[blk[i]][a[i]]++;
	}
	for (int i=1;i<=m;i++)	{
		int opt,l,r;
		read(opt);read(l);read(r);
		if (opt==0) update(l,r);
		if (opt==1) write(query(l,r));
	}
}

在这里插入图片描述

后续

接下来我会不断用C++来实现信奥比赛中的算法题、GESP考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值