【CF1097F Alex and a TV Show】

洛谷

题目描述

维护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\} AB={gcd(a,b)aA,bB}

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=1j=1AiBjxgcd(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=1i=1j=1AidBjdxd[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=1i=1j=1AidBjdxdkgcd(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=1xdi=1j=1kgcd(i,j)μ(k)AidBjd
好像我写的比较繁琐:
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=1xdk=1μ(k)i=1j=1AidkBjdk
常规 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=1xddTμ(dT)i=1ATij=1BTj

后面这个东西好像很妙 , 就是莫比乌斯反演的式子,那么结果是啥咧?
首先容易看出后面是一个多项式莫比乌斯变换后的结果,即:
A i = ∑ i ∣ d A d A_i=\sum_{i|d}A_d Ai=idAd
也就是倍数前缀和 , 记莫比乌斯变换后的结果为: 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=1xdT=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=1xiiTμ(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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值