题目描述
维护n个初始为空的可重集,支持以下操作:
1 x v:令集合x等于{v}
2 x y z:令集合x等于集合y与z的并
3 x y z:令集合x等于集合y与z的积, A ∗ B = { g c d ( a , b ) ∣ a ∈ A , b ∈ B } A∗B=\{gcd(a,b)∣a∈A,b∈B\} A∗B={gcd(a,b)∣a∈A,b∈B}
4 x v:询问v在集合x中出现次数模2的结果
Sol
个人感觉是一道非常好的题目。
题目中说了值域只有 7000 ,而且答案居然是对 2 取模??
b
i
t
s
e
t
bitset
bitset 跑不了了
顿时感觉是道简单题 :
1操作直接A[x].set(v);
2操作直接A[x]|=A[y];
4操作直接cout<<A[x][v];
3操作 …???什么鬼,这个是个啥玩意,好像做不了了,这个要怎么变换啊
显然先把这个玩意写成一个多项式 , 不然式子都推不下去了,设
A
(
x
)
A(x)
A(x)表示集合的多项式,其系数
A
i
A_i
Ai表示元素
i
i
i的出现次数模2的结果
那么推一波式子(下面求和的上界都懒得写了,多了也没有贡献):
A
(
x
)
∗
B
(
x
)
=
∑
i
=
1
∑
j
=
1
A
i
∗
B
j
∗
x
g
c
d
(
i
,
j
)
A(x)*B(x)=\sum_{i=1}\sum_{j=1}A_i*B_j*x^{gcd(i,j)}
A(x)∗B(x)=i=1∑j=1∑Ai∗Bj∗xgcd(i,j)
常规操作枚举
g
c
d
gcd
gcd:
A
(
x
)
∗
B
(
x
)
=
∑
d
=
1
∑
i
=
1
∑
j
=
1
A
i
d
∗
B
j
d
∗
x
d
[
g
c
d
(
i
,
j
)
=
1
]
A(x)*B(x)=\sum_{d=1}\sum_{i=1}\sum_{j=1}A_{id}*B_{jd}*x^d \big[gcd(i,j)=1\big]
A(x)∗B(x)=d=1∑i=1∑j=1∑Aid∗Bjd∗xd[gcd(i,j)=1]
这怕不是要莫比乌斯反演的节奏:
A
(
x
)
∗
B
(
x
)
=
∑
d
=
1
∑
i
=
1
∑
j
=
1
A
i
d
∗
B
j
d
∗
x
d
∑
k
∣
g
c
d
(
i
,
j
)
μ
(
k
)
A(x)*B(x)=\sum_{d=1}\sum_{i=1}\sum_{j=1}A_{id}*B_{jd}*x^d \sum_{k|gcd(i,j)}\mu(k)
A(x)∗B(x)=d=1∑i=1∑j=1∑Aid∗Bjd∗xdk∣gcd(i,j)∑μ(k)
交换求和顺序:
A
(
x
)
∗
B
(
x
)
=
∑
d
=
1
x
d
∑
i
=
1
∑
j
=
1
∑
k
∣
g
c
d
(
i
,
j
)
μ
(
k
)
∗
A
i
d
∗
B
j
d
A(x)*B(x)=\sum_{d=1}x^d\sum_{i=1}\sum_{j=1}\sum_{k|gcd(i,j)}\mu(k)*A_{id}*B_{jd}
A(x)∗B(x)=d=1∑xdi=1∑j=1∑k∣gcd(i,j)∑μ(k)∗Aid∗Bjd
好像我写的比较繁琐:
A
(
x
)
∗
B
(
x
)
=
∑
d
=
1
x
d
∑
k
=
1
μ
(
k
)
∑
i
=
1
∑
j
=
1
A
i
d
k
∗
B
j
d
k
A(x)*B(x)=\sum_{d=1}x^d\sum_{k=1}\mu(k) \sum_{i=1}\sum_{j=1}A_{idk}*B_{jdk}
A(x)∗B(x)=d=1∑xdk=1∑μ(k)i=1∑j=1∑Aidk∗Bjdk
常规
T
=
d
k
T=dk
T=dk:
A ( x ) ∗ B ( x ) = ∑ d = 1 x d ∑ d ∣ T μ ( T d ) ∑ i = 1 A T i ∑ j = 1 B T j A(x)*B(x)=\sum_{d=1}x^d\sum_{d|T}\mu(\frac{T}{d}) \sum_{i=1}A_{Ti}\sum_{j=1}B_{Tj} A(x)∗B(x)=d=1∑xdd∣T∑μ(dT)i=1∑ATij=1∑BTj
后面这个东西好像很妙 , 就是莫比乌斯反演的式子,那么结果是啥咧?
首先容易看出后面是一个多项式莫比乌斯变换后的结果,即:
A
i
=
∑
i
∣
d
A
d
A_i=\sum_{i|d}A_d
Ai=i∣d∑Ad
也就是倍数前缀和 , 记莫比乌斯变换后的结果为:
F
M
T
(
A
)
FMT(A)
FMT(A) , 其第
i
i
i次方项系数为
F
M
T
(
A
)
i
FMT(A)_i
FMT(A)i,后面就是多项式点值相乘的结果! , 然后
I
F
M
T
IFMT
IFMT,也就是莫比乌斯反演还原多项式:
A ( x ) ∗ B ( x ) = ∑ d = 1 x d ∑ T = 1 μ ( T d ) F M T ( A ) T F M T ( B ) T A(x)*B(x)=\sum_{d=1}x^d\sum_{T=1}\mu(\frac{T}{d})FMT(A)_TFMT(B)_T A(x)∗B(x)=d=1∑xdT=1∑μ(dT)FMT(A)TFMT(B)T
两边同时做 F M T FMT FMT:
F
M
T
(
A
(
x
)
∗
B
(
x
)
)
=
F
M
T
(
A
)
∗
F
M
T
(
B
)
FMT(A(x)*B(x))=FMT(A)*FMT(B)
FMT(A(x)∗B(x))=FMT(A)∗FMT(B)
那么
F
M
T
FMT
FMT后就可以多项式按位相乘了
显然
F
M
T
FMT
FMT 后的加法还是能直接加,乘法的话稍微想想就知道是按位与的操作。完美解决所有操作…
慢着 , 发现直接这样是没有办法询问的 , 必须要进行逆莫比乌斯变换,也就是莫比乌斯反演还原多项式,但是暴力还原是要枚举倍数的,没有办法保证复杂度。
问题就只有求解:
A ( x ) = I F M T ( A ) = ∑ i = 1 x i ∑ i ∣ T μ ( T i ) F M T ( A ) T A(x)=IFMT(A)=\sum_{i=1}x^i\sum_{i|T}\mu(\frac{T}{i})FMT(A)_T A(x)=IFMT(A)=i=1∑xii∣T∑μ(iT)FMT(A)T
其实我们没有必要真的还原,因为它只是询问一个数的出现次数又不是所有数 , 说明白点就是我们只需要还原后多项式第 i i i 次方项的系数 , i i i 是询问的数
那么就是要管后面那一坨东西了,对于一个
i
i
i 而言,怎么快速求出后面的东西?
发现这玩意也很像多项式系数对应相乘 , 但是要求和 , 那不就完了么 , 我们直接预处理出每一个
i
i
i 对应的
μ
\mu
μ 的
b
i
t
s
e
t
bitset
bitset , 就是把
i
i
i的所有倍数的第
i
i
i 项置为
μ
(
T
i
)
\mu(\frac{T}{i})
μ(iT) , 然后查询的时候按位乘上就是与一下 , 最后
c
o
u
n
t
(
)
count()
count()一下模个2就是答案了。
代码很短的:
#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;
}
const int MAXN=7001;
const int N=1e5+10;
const int MAXQ=1e6+10;
typedef bitset<MAXN> Bit;
Bit A[N];
Bit val[MAXN];
int mu[MAXN];
Bit Mu[MAXN];
inline void Sieve(){
for(int i=mu[1]=1,j;i<=7000;++i) for(val[i].set(i),j=i+i;j<=7000;j+=i) mu[j]^=mu[i],val[j].set(i);
for(int i=1;i<=7000;++i)for(int j=i,k=1;j<=7000;j+=i,++k)if(mu[k]) Mu[i].set(j);
}
int n,m;
int main()
{
init(n),init(m);Sieve();
for(int i=1;i<=m;++i) {
int opt,x,y,z;
init(opt);
if(opt==1) {init(x),init(y);A[x]=val[y];}
else if(opt==2) {init(x),init(y),init(z);A[x]=A[y]^A[z];}
else if(opt==3) {init(x),init(y),init(z);A[x]=A[y]&A[z];}
else if(opt==4) {
init(x),init(y);
Bit ans=A[x];ans&=Mu[y];
printf("%d",ans.count()&1);
}
}
return 0;
}