Description
一开始你有一个空集,集合可以出现重复元素,然后有 Q 个操作:add s
在集合中加入数字 s 。del s
在集合中删除数字 s 。保证 s 存在。如果有多个 s,只删除一个即可。cnt s
查询满足 a&s=a 条件的 a 的个数。
Input
第一行一个整数 Q 接下来 Q 行,每一行都是 3 个操作中的一个。Output
对于每个 cnt 操作输出答案。Sample Input
7
add 11
cnt 15
add 4
add 0
cnt 6
del 4
cnt 15Sample Output
1
2
2Hint
对于 30% 的数据满足:1≤n≤1000;
对于 100% 的数据满足:1≤n≤200000;0<s<216 。题解:分块
容易发现对于每一个cnt操作的s,满足的数a前后半段互相之间无影响。
考虑将每一个输入的a分为(28∗k1+k2(k2<28)),即k1为a的前八位,k2为a的后八位。
维护f[pre][suf],表示满足k1&pre=k1中suf=k2的个数,加入a时更新满足条件的区间,查询s时,先找到s的前缀pre1,由以上性质可得a[pre1]中所有a 的前半段都满足,这时只需在此区间中循环查找suf满足的个数即可。
时间复杂度:
- Code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int Maxn=(1<<8);
inline int read()
{
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
return i*f;
}
int n,a[Maxn+50][Maxn+50];
char ch[5];
int buf[30];
inline void W(int x)
{
if(x==0)
{
putchar('0');
return;
}
while(x)buf[++buf[0]]=x%10,x/=10;
while(buf[0])putchar(buf[buf[0]--]+'0');
return;
}
int main()
{
n=read();
while(n--)
{
scanf("%s",ch+1);
int s=read();
int t=s>>8,res=s-(t<<8);
switch(ch[1])
{
case 'a':
{
for(int i=0;i<Maxn;i++)
{
if((t&i)==t)a[i][res]++;
}
break;
}
case 'c':
{
int r=0;
for(int i=0;i<Maxn;i++)
{
if((i&res)==i)r+=a[t][i];
}
W(r);
putchar('\n');
break;
}
case 'd':
{
for(int i=0;i<Maxn;i++)
{
if((t&i)==t)a[i][res]--;
}
break;
}
}
}
return 0;
}