【牛客练习赛85】D 数学家的谜题
【牛客练习赛85】D 数学家的谜题
题目简要:
这个问题是这样的,首先给出 \(n\) 个数字,第 \(i\) 个数字为 \(a[i]\)。接下来进行 \(m\) 次操作,每次操作的类型如下:
1:将 \(a[id]\) 的值改为\(x\)。
2:令 \(t=a[l]×a[l+1]×...×a[r−1]×a[r]\),求 \(t\) 能被多少个不同的素数整除。
注意到 \(a_i\) 的值域并不大,只有 \(10^5\)。而且题目要求的还是不同的质因子。我们可以先跑一边质数筛看看 \(10^5\) 以内有多少个质数。发现结果其实只有 9000 多个。不到 10000 个。考虑二进制枚举。这就有引出一个黑科技了 bitset
。这个东西通过固定的优化使得他的速度不仅是 int
的 \(\dfrac{1}{w}\) 倍,空间也是 \(\dfrac{1}{w}\) 倍(\(w\) 在这里指计算机的位数,我们可以默认为 \(w=32\))。具体使用方法这里不再赘述了,可以自行百度。这种区间查询问题我们第一反应肯定是线段树。那就直接暴力一点,我们建一个线段树,每个节点存一个 bitset
。空间大概是相当于开了 \(n\times 4\times10000\times\dfrac{1}{w}\) 个 int
。时间复杂度我们下面再讨论。
首先是修改操作,修改是单点修改。我们直接我们找到待修改的点,然后修改他的bitset。分解质因数同样需要 \(\sqrt{x}\) 的时间。单次复杂度大约是 \(O(\sqrt{x}+log(n)\times\dfrac{10000}{w})\)。(\(x\) 表示值域)
查询就是普通的区间查询,对于查询过来的答案我们进行 或 这个操作。时间大约是 \(O(\log{n}\times\dfrac{10000}{w})\) 。
总的来说,时间复杂度就是 \(O(n\times\sqrt{x}\;+n\times\log(n)\times\dfrac{10000}{w})\)。这个复杂度我们是完全可以接受的。
代码如下:
#include<bits/stdc++.h>
#define ls(k) (k<<1)
#define rs(k) (k<<1|1)
using namespace std;
const int MAXN = 5e4+5;
int n,m,p[MAXN],a[MAXN];
bool ip[100005];
struct Tree
{
bitset<10000> v;
}t[MAXN<<2];
bitset<10000> ans;
void pushup(int k)
{
t[k].v=t[ls(k)].v|t[rs(k)].v;
}
void build(int k,int l,int r)
{
if(l==r)
{
int num=a[l],up=sqrt(a[l])+1;
for(int i=1;i<=p[0];++i)
{
if(p[i]>up||num==1) continue;
if(num%p[i]==0) t[k].v[i]=1;
while(num%p[i]==0) num/=p[i];
}
if(num!=1)
{
int pos=lower_bound(p+1,p+1+p[0],num)-p;
t[k].v[pos]=1;
}
return ;
}
int mid=l+r>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
pushup(k);
}
void upd(int pos,int k,int l,int r,int x)
{
if(l==r&&l==pos)
{
t[k].v.reset();
t[k].v|=ans;
return ;
}
int mid=l+r>>1;
if(pos<=mid) upd(pos,ls(k),l,mid,x);
else upd(pos,rs(k),mid+1,r,x);
pushup(k);
}
void query(int ll,int rr,int k,int l,int r)
{
if(ll<=l&&r<=rr)
{
ans|=t[k].v;
return ;
}
int mid=l+r>>1;
if(ll<=mid) query(ll,rr,ls(k),l,mid);
if(rr>mid) query(ll,rr,rs(k),mid+1,r);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=2;i<=100000;++i)
{
if(p[i]) continue;
for(int j=2*i;j<=100000;j+=i)
p[j]=1;
p[++p[0]]=i;
}
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
build(1,1,n);
for(int i=1;i<=m;++i)
{
int op;
scanf("%d",&op);
if(op==1)
{
int id,x;
scanf("%d %d",&id,&x);
int num=x,up=sqrt(x)+1;
ans.reset();
for(int i=1;i<=p[0];++i)
{
if(p[i]>up) continue;
if(num==1) break;
if(num%p[i]==0) ans[i]=1;
while(num%p[i]==0) num/=p[i];
}
if(num!=1)
{
int pos=lower_bound(p+1,p+1+p[0],num)-p;
ans[pos]=1;
}
upd(id,1,1,n,x);
}
else
{
int l,r;
scanf("%d %d",&l,&r);
ans.reset();
query(l,r,1,1,n);
printf("%d\n",ans.count());
}
}
return 0;
}
总结:
主要是 bitset
的用法,这个黑科技的优化还是比较大的,很多题目甚至还可以用 bitset
暴力艹 std
。主要是 \(\dfrac{1}{w}\) 很关键,计算时间复杂度时一定要算进去。我就是因为计算错误导致没去写这种写法,想来还是很气的。