数学家的迷题
题意:
1:将的值改为。
2:令,求t能被多少个不同的素数整除。
题解:
本题有两种解法,带修莫队和线段树,带修莫队的话需要开个O3提提速,不然会T一个点。
1.带修莫队
题目不是5e4的范围吗,带修莫队会卡时间?
因为莫队移动的操作时间复杂度为O(1),但是这个移动的时间复杂度却不是O(1),应该是一个10左右的常数的大小,再加上vector本身就有点慢,所以会有点卡时间。
带修莫队的话,其实就可以当场是一个比较模板的题目了,只不过一个位置上的数字变成了好几个而已。
代码:
#pragma GCC optimize(3) //#pragma GCC optimize(3,"Ofast","inline") #include<bits/stdc++.h> #define endl '\n' using namespace std; const int maxn=1e5+10; bool vis[maxn]; int prime[maxn], tot; void get_prime(int n) { vis[1] = true; for(int i = 2; i <= n; ++ i) { if (!vis[i]) prime[++tot] = i; for(int j = 1; prime[j] <= n/i; ++ j) { vis[prime[j] * i] = true; if (i % prime[j] == 0) break; } } } vector<int> pri[maxn]; struct node{ int l,r,id,t; }q[maxn]; pair<int,vector<int> > c[maxn]; int be[maxn]; int cntq,cntc,sz; int cnt[maxn]; struct rule{ bool operator ()(const node & x,const node & y)const{ if(be[x.l]!=be[y.l])return x.l<y.l; if(be[x.r]!=be[y.r])return x.r<y.r; return x.t<y.t; } }; int sum; int l=0,r,t; void add(int x){ for(auto i:pri[x]){ if(++cnt[i]==1) sum++; } } void sub(int x){ for(auto i:pri[x]){ if(--cnt[i]==0) sum--; } } void upt(int x){ if(c[x].first>=l&&c[x].first<=r){ for(auto i:pri[c[x].first]){ if(--cnt[i]==0) sum--; } for(auto i:c[x].second){ if(++cnt[i]==1) sum++; } } swap(pri[c[x].first],c[x].second); } int ans[maxn]; int main(){ get_prime(100000); int n,m; ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>m; sz=pow(n,2.0/3.0); for(int i=1;i<=n;i++){ int x; cin>>x; be[i]=i/sz+1; for(int k=1;k<=tot;k++){ if(x==1) break; if(vis[x]==false){ pri[i].push_back(x); break; } bool flag=false; while(x%prime[k]==0){ x/=prime[k]; flag=true; } if(flag){ pri[i].push_back(prime[k]); } } } for(int i=1;i<=m;i++){ int opt,x,y; cin>>opt>>x>>y; if(opt==1){ c[++cntc].first=x; //c[cntc].second=y; for(int k=1;k<=tot;k++){ if(y==1) break; if(vis[y]==false){ c[cntc].second.push_back(y); break; } bool flag=false; while(y%prime[k]==0){ y/=prime[k]; flag=true; } if(flag){ c[cntc].second.push_back(prime[k]); } } } else{ q[++cntq].l=x; q[cntq].r=y; q[cntq].t=cntc; q[cntq].id=cntq; } } sort(q+1,q+cntq+1,rule()); for(int i=1;i<=cntq;i++){ while(r<q[i].r) add(++r); while(l>q[i].l) add(--l); while(r>q[i].r) sub(r--); while(l<q[i].l) sub(l++); while(t<q[i].t) upt(++t); while(t>q[i].t) upt(t--); ans[q[i].id]=sum; } for(int i=1;i<=cntq;i++) cout<<ans[i]<<endl; }
2.线段树
线段树上每个点维护一个bitset即可。
最原始的线段树是求得和,这里的话把求和改成求或即可。
注意要压缩一下bitset的使用空间,1e5大约有1e4个素数,我们不能开1e5的bitset,我们只需要开1e4的bitset即可,把1e4的素数按照大小安排在每个位置,类似于离散化。
#pragma GCC optimize(2) #include<bits/stdc++.h> #define endl '\n' //#define ll long long using namespace std; const int maxn=1e5+10; bitset<10000> s[maxn*2]; bool vis[maxn]; int prime[maxn], tot; void get_prime(int n) { vis[1] = true; for(int i = 2; i <= n; ++ i) { if (!vis[i]) prime[++tot] = i; for(int j = 1; prime[j] <= n/i; ++ j) { vis[prime[j] * i] = true; if (i % prime[j] == 0) break; } } } vector<int> pri[maxn]; int mp[maxn]; void build(int node,int start,int ends){ if(start==ends){ for(auto i:pri[start]){ s[node][mp[i]]=1; //cout<<" debug "<<mp[i]<<endl; } return ; } int mid=(start+ends)/2; build(node*2,start,mid); build(node*2+1,mid+1,ends); s[node]=s[node*2+1]|s[node*2]; } void update(int node,int start,int ends,int pos){ if(start==pos&&ends==pos){ s[node].reset(); for(auto i:pri[start]){ s[node][mp[i]]=1; } return ; } int mid=(start+ends)/2; if(pos<=mid) update(node*2,start,mid,pos); else update(node*2+1,mid+1,ends,pos); s[node]=s[node*2+1]|s[node*2]; } bitset<10000> query(int node,int start,int ends,int l,int r){ if(l<=start&&ends<=r){ return s[node]; } int mid=(start+ends)/2; bitset<10000> res; if(l<=mid) res|=query(node*2,start,mid,l,r); if(mid<r) res|=query(node*2+1,mid+1,ends,l,r); return res; } int main(){ get_prime(100000); int n,m; cin>>n>>m; for(int i=1;i<=tot;i++){ mp[prime[i]]=i; } for(int i=1;i<=n;i++){ int x; cin>>x; for(int k=1;k<=tot;k++){ if(x==1) break; if(vis[x]==false){ pri[i].push_back(x); break; } bool flag=false; while(x%prime[k]==0){ x/=prime[k]; flag=true; } if(flag){ pri[i].push_back(prime[k]); } } } build(1,1,n); for(int i=0;i<m;i++){ int opt,x,y; cin>>opt>>x>>y; if(opt==1){ pri[x].clear(); for(int k=1;k<=tot;k++){ if(y==1) break; if(vis[y]==false){ pri[x].push_back(y); break; } bool flag=false; while(y%prime[k]==0){ y/=prime[k]; flag=true; } if(flag){ pri[x].push_back(prime[k]); } } update(1,1,n,x); } else{ bitset<10000> ans=query(1,1,n,x,y); int cnt=ans.count(); cout<<cnt<<endl; } } }
题解 文章被收录于专栏
主要写一些题目的题解