[NOIP2019模拟赛]数数(gcd)
题目大意:
求l~r中有多少数与x互质,带单点修改
分析:
两个30的部分分很好打:
·n<=1000暴力O(nq)就好了
·$a_i<=100$用树状数组维护每个x的前缀和就好了
100分做法有两种,一种是莫比乌斯反演(我不会),还有一种就是bitset乱搞
我们先筛法筛出所有质数(大概不到10000个)然后对于每个质数建立一个bitset,表示对于第i个数在有第j个质数这个质因子
求有多少数与x互质转换为有多少数与x不互质就好了
对于每次询问把所有x的质因子找出来然后把这些数的bitset或起来再用类似前缀和的方法维护即可
时间复杂度$O(\frac{n*Q*logn}{32})$显然跑不满,所以卡一卡就过了(跑的比莫比乌斯反演快)
代码:(这里把两种部分分的打法也都写上了)
1 #pragma GCC optimize("Ofast") 2 #include<bits/stdc++.h> 3 #define lowbit(x) (x&(-x)) 4 using namespace std; 5 inline int read(){ 6 int ans=0,f=1;char chr=getchar(); 7 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 8 while(isdigit(chr)) {ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();} 9 return ans*f; 10 }const int M = 5e4+5; 11 int n,m,a[M],Q,ans,gcd[101][101]; 12 struct P{ 13 int s[M]; 14 void Update(int x,int a){for(;x<=n;x+=lowbit(x)) s[x]+=a;} 15 int Query(int x){int res=0;for(;x;x-=lowbit(x)) res+=s[x];return res;} 16 }t[101]; 17 //-------------------------------------------------第一个30pts -------------------------------------------------- 18 inline void Solve_1(){//n*Q<=1000000 19 while(Q--){ 20 int opt=read(),x; 21 if(opt==1) x=read(),a[x]=read(); 22 else{ 23 int l=read(),r=read(); 24 x=read();ans=0; 25 for(int i=l;i<=r;i++) 26 if(__gcd(x,a[i])==1) 27 ++ans; 28 printf("%d\n",ans); 29 } 30 } 31 } 32 //---------------------------------------第二个30pts ----------------------------------------------------- 33 inline void Solve_2(){//Ai<=100 34 for(int i=1;i<=100;++i) 35 for(int j=1;j<=100;++j) 36 gcd[i][j]=__gcd(i,j); 37 for(int i=1;i<=100;i++) 38 for(int j=1;j<=n;j++) 39 if(gcd[i][a[j]]==1) 40 t[i].Update(j,1); 41 while(Q--){ 42 int opt=read(); 43 if(opt==1){ 44 int x=read(),y=read(),tt=a[x]; 45 a[x]=y; 46 for(int i(1);i<=100;++i){ 47 if(gcd[a[x]][i]!=1&&gcd[tt][i]==1) 48 t[i].Update(x,-1); 49 if(gcd[a[x]][i]==1&&gcd[tt][i]!=1) 50 t[i].Update(x,1); 51 } 52 }else{int l=read(),r=read(),x=read(); 53 printf("%d\n",t[x].Query(r)-t[x].Query(l-1)); 54 } 55 } 56 }bitset<M>s[10000],now; 57 int v[M*2],pri[M*2],tot; 58 //----------------------------------------通解-------------------------------------------- 59 inline void Get_Pri(){ 60 for(int i=2;i<=100000;i++){ 61 if(!v[i]) v[i]=i,pri[++tot]=i; 62 for(int j=1;j<=tot;j++){ 63 if(pri[j]>100000/i||pri[j]>v[i]) break; 64 v[i*pri[j]]=pri[j]; 65 } 66 } 67 } 68 inline void Update(int pos,int x,int y){ 69 int i=1; 70 while(1){ 71 if(pri[i]*pri[i]>x) break; 72 if(x%pri[i]==0){ 73 s[i][pos]=y; 74 while(x%pri[i]==0) x/=pri[i]; 75 }i++; 76 }if(x>1)s[lower_bound(pri+1,pri+tot+1,x)-pri][pos]=y; 77 } 78 inline void Solve(){ 79 Get_Pri(); 80 for(int i=1;i<=n;i++) Update(i,a[i],1); 81 while(Q--){ 82 int opt=read(); 83 if(opt==1){ 84 int x=read(),y=read(); 85 Update(x,a[x],0),Update(x,y,1); 86 a[x]=y; 87 }else{ 88 int l=read(),r=read(),x=read(); 89 int i=1;now=0; 90 while(1){ 91 if(pri[i]*pri[i]>x) break; 92 if(x%pri[i]==0){ 93 now|=s[i]; 94 while(x%pri[i]==0) x/=pri[i]; 95 }i++; 96 }if(x>1) now|=s[lower_bound(pri+1,pri+tot+1,x)-pri]; 97 int Ans=r-l+1-(now>>l).count()+(now>>(r+1)).count(); 98 printf("%d\n",Ans); 99 } 100 } 101 } 102 int main(){ 103 freopen("gcd.in","r",stdin); 104 freopen("gcd.out","w",stdout); 105 n=read(); 106 for(int i=1;i<=n;++i) a[i]=read(); 107 Q=read(); 108 // if(n*Q<=1000000) Solve_1(); 109 // else Solve_2(); 110 Solve(); 111 return 0; 112 }