题目大意
对于集合 a a a,定义集合 S ( a ) S(a) S(a) 表示集合 a a a 生成的集合,生成方式为通过以下步骤任意多次:
- 初始, S ( a ) = a S(a)=a S(a)=a;
- 若存在 x , y ∈ S ( a ) x,y \in S(a) x,y∈S(a),但是 x ⊕ y ∉ S ( a ) x\oplus y \not \in S(a) x⊕y∈S(a),将其插入到 S ( a ) S(a) S(a) 中。
现在给定集合 a , b a,b a,b,你需要维护一个数据结构,支持以下操作,共 m m m 次:
- 1 x 1\ x 1 x,表示插入 x x x 到集合 a a a 中,保证插入之前 x ∉ a x \not\in a x∈a
- 2 x 2\ x 2 x,表示插入 x x x 到集合 b b b 中,保证插入之前 x ∉ b x \not\in b x∈b
- 3 x 3\ x 3 x,表示从集合 a a a 中删除元素 x x x,保证删除之前 x ∈ a x \in a x∈a
- 4 x 4\ x 4 x,表示从集合 b b b 中删除元素 x x x,保证删除之前 x ∈ b x \in b x∈b
- 5 5 5,表示询问:输出 ∣ S ( a ) ∪ S ( b ) ∣ m o d 998244353 |S(a) \cup S(b)| \bmod 998244353 ∣S(a)∪S(b)∣mod998244353,即集合并的元素个数
∣
a
∣
,
∣
b
∣
≤
1
0
5
,
m
≤
2
×
1
0
5
|a|,|b| \le 10^5,\ \ m \leq 2\times 10^5
∣a∣,∣b∣≤105, m≤2×105,所有的集合元素
∈
[
0
,
2
63
)
\in [0,2^{63})
∈[0,263)
多测,时限比较迷。。反正
O
(
n
log
2
x
)
O(n \log^2 x)
O(nlog2x) 跑不过
\\
\\
\\
题解
首先,这个
S
(
a
)
S(a)
S(a)、
S
(
b
)
S(b)
S(b) 肯定是考虑维护其线性基了。
这里要用到带删除的线性基,删除有在线和离线两种方式,在线是真正地实现删除操作,看这里学一学;离线是计算出每个元素的过期时间,然后线性基插入操作的时候用更新式的插入法,保持基元都是最新的,参考这题。
然后看怎么计算答案。容斥一下有
a
n
s
=
∣
S
(
a
)
∣
+
∣
S
(
b
)
∣
−
∣
S
(
a
)
∩
S
(
b
)
∣
ans=|S(a)|+|S(b)|-|S(a) \cap S(b)|
ans=∣S(a)∣+∣S(b)∣−∣S(a)∩S(b)∣,那怎么算
∣
S
(
a
)
∩
S
(
b
)
∣
|S(a) \cap S(b)|
∣S(a)∩S(b)∣ 呢?
可以想到两种方法,一是直接求交,拿
S
(
a
)
S(a)
S(a) 的每个基元放到
S
(
b
)
S(b)
S(b) 里去看看能不能被表示出来,能就说明它是
S
(
a
)
S(a)
S(a) 和
S
(
b
)
S(b)
S(b) 交空间的基。(qaq为啥我觉得一点都不显然啊) 但这样是
O
(
m
log
2
x
)
O(m \log^2 x)
O(mlog2x) 的,被卡掉了。
第二种方法是再容斥一下,
S
(
a
)
∩
S
(
b
)
S(a) \cap S(b)
S(a)∩S(b) 的基元个数 =
S
(
a
)
S(a)
S(a) 基元个数 +
S
(
b
)
S(b)
S(b) 基元个数 -
S
(
a
∪
b
)
S(a \cup b)
S(a∪b) 基元个数。(qaq这个也很不显然啊为啥你们就觉得显然了啊) 于是这样就只需要维护三个线性基:
S
(
a
)
S(a)
S(a)、
S
(
b
)
S(b)
S(b)、
S
(
a
)
∪
S
(
b
)
=
S
(
a
∪
b
)
S(a) \cup S(b)=S(a\cup b)
S(a)∪S(b)=S(a∪b),就变成
O
(
m
log
x
)
O(m \log x)
O(mlogx) 的了。
代码
#include<cstdio>
#include<cstring>
#include<map>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<ULL,int> pr;
const int maxop=5e5+5, MX=62;
const int inf=1e9;
const LL mo=998244353;
struct OP{
ULL x;
bool ab,ty;
int endt,t;
};
int n,m,q,op0;
OP op[maxop];
map<ULL,int> Sa,Sb;
pr Ba[MX+2],Bb[MX+2],Bab[MX+2];
void add(pr *B,pr x)
{
fd(i,MX,0) if ((x.first>>i)&1)
{
if (B[i].first)
{
if (B[i].second<x.second) swap(B[i],x);
x.first^=B[i].first;
if (!x.first) break;
} else
{
B[i]=x;
break;
}
}
}
int qry(pr *B,int endt)
{
int cnt=0;
fo(i,0,MX) cnt+=(B[i].first>0 && B[i].second>endt);
return cnt;
}
LL er[MX+5];
int main()
{
er[0]=1;
fo(i,1,MX+2) er[i]=(er[i-1]<<1)%mo;
while (scanf("%d %d",&n,&m)!=EOF)
{
Sa.clear();
Sb.clear();
op0=0;
fo(i,1,n)
{
ULL x;
scanf("%llu",&x);
op[++op0]=(OP){x,0,0,inf,i};
Sa[x]=op0;
}
fo(i,1,m)
{
ULL x;
scanf("%llu",&x);
op[++op0]=(OP){x,1,0,inf,n+i};
Sb[x]=op0;
}
scanf("%d",&q);
fo(i,1,q)
{
int ty; ULL x;
scanf("%d",&ty);
if (ty<=4) scanf("%llu",&x);
if (ty==1)
{
op[++op0]=(OP){x,0,0,inf,n+m+i};
Sa[x]=op0;
} else if (ty==2)
{
op[++op0]=(OP){x,1,0,inf,n+m+i};
Sb[x]=op0;
} else if (ty==3)
{
op[Sa[x]].endt=n+m+i;
} else if (ty==4)
{
op[Sb[x]].endt=n+m+i;
} else
{
op[++op0]=(OP){0,0,1,0,n+m+i};
}
}
fo(i,0,MX) Ba[i]=Bb[i]=Bab[i]=pr(0,0);
fo(i,1,op0) if (!op[i].ty)
{
if (op[i].ab==0) add(Ba,make_pair(op[i].x,op[i].endt));
else add(Bb,make_pair(op[i].x,op[i].endt));
add(Bab,make_pair(op[i].x,op[i].endt));
} else
{
int x=qry(Ba,op[i].t), y=qry(Bb,op[i].t), z=qry(Bab,op[i].t);
LL ans=((er[x]+er[y]-er[x+y-z])%mo+mo)%mo;
printf("%lld\n",ans);
}
}
}