abc349F 题解
我都自闭了,赛后一分钟调出来。
题意
给定一个长度为 \(n\) 的序列,现在从原序列中取一个子序列,使得子序列中元素的最小公倍数为 \(m\)。求有多少个子序列满足条件,\(\mod 998244353\)。
\(1\le n\le 2\times 10^5\)
\(1\le A_i,m\le 10^{16}\)
思路
一个很明显的性质就是,如果先将 \(m\) 分解质因数成 \(\prod p_k^{c_k}\) 的形式,子序列 \(U\) 中的数也分解质因数,那么明显,对于一个子序列成为答案的充要条件是:
- 对于 \(\forall p_k\) ,有 \(\max\limits_{v\in U} c_v=c_k\)
- \(m\) 的质因数集合等于子序列所有数子集的并集
通俗的说,就是对于 \(m\) 的每个质因数,能在子序列找到一个数,使它们的最高次幂相同。
进行一些适当的预处理,排除掉不可能贡献的数。
这个时候,问题转化为:
- 有若干个序列,从中选取若干个序列,使得对于 \(\forall i,\max a_i=b_i\)
举个例子:
不妨令 \(m=24\) ,此时 \(m=2^3\times 3\)
那么,\(6=2\times 3\),\(12=2^2\times 3\),\(8=2^3\)
那么原问题化为:给定 \(a=\{3,1\}\),\(3\) 个序列 \(\{1,1\},\{2,1\},\{3,0\}\) ,很明显只有 \(3\) 种方案满足要求。
那么我们知道,只有 \(a_i=b_i\),才会产生合法的贡献。我们计如果第 \(i\) 个数第 \(j\) 位满足这个条件,那么 \(d_{i,j}=1\),否则为 \(0\) 。
现在,问题转化成一个通俗模型:
- 给定 \(n\) 个长度为 \(k\) 的 \(01\) 串,求它们或起来全为 \(1\) 的方案数。
很明显有个 \(O(n2^k)\) 做法,然后被出题人卡掉了。
蒟蒻不会高级算法,只会朴素容斥。
思路:正难则反,钦定其中某些位数为 \(0\),求方案。
这个很好做,把这些位数全为 \(0\) 的数个数求出来 为\(cnt\),然后贡献就是 \(2^{cnt}\) 。
然后计算过程发现就是求交集而已,直接上一个 bitset
暴力优化即可。然后就过了。时间复杂度 \(O(\frac{n2^k}{w})\) 。
code
#include<bits/stdc++.h>
#define N 200005
#define ll long long
using namespace std;
const int mod=998244353;
int n;
ll m,p[N];
int t[N],tot;
int len;
bitset<N> g[16],f;
ll pw[N],ans;
int main()
{
scanf("%d%lld",&n,&m);
for(ll i=2;i<=sqrt(m);i++)
{
if(m%i) continue;
p[++tot]=i;
while(!(m%i)) m/=i,t[tot]++;
}
if(m^1) p[++tot]=m,t[tot]=1;
f[0]=1;
for(int i=1;i<=n;i++)
{
int mask=0,tag=0;
ll x;
scanf("%lld",&x);
for(int j=1;j<=tot;j++)
{
int v=0;
while(!(x%p[j])) x/=p[j],v++;
if(v>t[j])
{
tag=1;
break;
}
if(v^t[j]) mask|=(1<<j-1);
}
if(x^1||tag) continue;
++len;
for(int j=0;j<tot;j++)
if(mask&(1<<j)) g[j].set(len);
}
pw[0]=1;
for(int i=1;i<=len;i++) pw[i]=(pw[i-1]*2)%mod;
ans=pw[len];
for(int i=1;i<(1<<tot);i++)
{
int cnt=0;
f.set();
for(int j=0;j<tot;j++)
if(i&(1<<j)) f&=g[j],cnt++;
if(!(cnt&1)) ans=(ans+pw[f.count()])%mod;
else ans=(ans+mod-pw[f.count()])%mod;
}
if(!tot) ans=(ans-1+mod)%mod;
printf("%lld",ans);
return 0;
}
跑的飞快,最慢的点 314ms
。