小伙伴的数据结构 | ||||||
| ||||||
Description | ||||||
给你一些二进制串,我们有对这些数有两种操作。 'I i j' 将[i,j]内的所有数取反(0变1,1变0) 'Q i' 输出第i个数是多少 下标从1开始,输入串允许有前导0
| ||||||
Input | ||||||
输入包含一个整数T(T<=10000),表示测试样例个数。 每组样例的第一行有一个长度为n的01串(n<=20000) 第二行有一个整数Q(Q<=10000)代表将会有多少个操作
| ||||||
Output | ||||||
输出每一个Q i的结果 | ||||||
Sample Input | ||||||
2 0011001100 6 I 1 10 I 2 7 Q 2 Q 1 Q 7 Q 5 1011110111 6 I 1 10 I 2 7 Q 2 Q 1 Q 7 Q 5
| ||||||
Sample Output | ||||||
Case 1: 0 1 1 0 Case 2: 0 0 0 1
| ||||||
Author | ||||||
小伙伴们@哈商大 |
思路:
1、首先看到时间限制500ms,看到Q查询次数比较大,再看题干中的两种操作,区间更新+单点查询,无疑需要使用树状数组或者是线段树之类的来优化查询和更新的操作。
2、对于取反操作,如果能够知道当前这个位子一共改变了多少次,其实也就能知道现在这个位子上的数字是几,如果改变了偶数次,那么这个位子上的数字不变,否则取反。
3、对于统计这个位子一共改变了多少次,归根到底是对于区间更新的操作的一种统计,辣么如何进行区间更新呢?如果您使用的是线段树来解,这里就可以跳过了,直接区间更新+懒惰标记即可。如果您使用的是树状数组来解,这里不难理解:
假设原串是:
01010101;
假设有一种操作,I 1 5表示对【1,5】取反,假设我们有一个数组a【8】,表示每个位子都操作了多少次,暴力情况下,我们使得:a【1】=a【2】=a【3】=a【4】=a【5】=1;对于每个点都进行一次操作,其实我们不妨优化把一堆值更新优化到两个值更新:a【1】=1;a【6】=-1;这样,假设我们询问4号位子上一共改变了多少次的时候,我们可以从a【1】+a【】...+a【4】来查询4号位子上有多少次操作,显然值为1,没有问题。那么我们要询问7号位子上一共改变了多少次的时候,我们照旧a【1】+a【2】+...+a【7】来查询7号位子上有多少次操作,显然值为0,也没有问题。这样我们就可以用树状数组来完成这两个操作了,区间值加和查询+点更新值。
4、处理好字符串,问题就解决了。
AC代码:
#include<stdio.h>
#include<string.h>
using namespace std;
char tmp[20050];
int a[20050];
int tree[1000005];//树
int n;
int lowbit(int x)//lowbit
{
return x&(-x);
}
int sum(int x)//求和求的是比当前数小的数字之和,至于这里如何实现,很简单:int sum=sum(a[i]);
{
int sum=0;
while(x>0)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
void add(int x,int c)//加数据。
{
while(x<=n)
{
tree[x]+=c;
x+=lowbit(x);
}
}
int main()
{
int t;
int kase=0;
scanf("%d",&t);
while(t--)
{
memset(tree,0,sizeof(tree));
scanf("%s",tmp);
n=strlen(tmp);
for(int i=1;i<=n;i++)
{
a[i]=tmp[i-1]-'0';
}
int q;
scanf("%d",&q);
printf("Case %d:\n",++kase);
while(q--)
{
char op[5];
scanf("%s",op);
if(op[0]=='Q')
{
int x;
scanf("%d",&x);
int cishu=sum(x);
//printf("%d\n",cishu);
if(cishu%2==0)
{
printf("%d\n",a[x]);
}
else printf("%d\n",1-a[x]);
}
else
{
int x,y;
scanf("%d%d",&x,&y);
add(x,1);
add(y+1,-1);
}
}
}
}