牛客练习赛85 数学家的迷题 (带修莫队/线段树)

Posted 昵称很长很长真是太好了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛85 数学家的迷题 (带修莫队/线段树)相关的知识,希望对你有一定的参考价值。

题意:
1:将 a [ i d ] a[id] a[id]的值改为 x x x
2:令 t = a [ l ] × a [ l + 1 ] × . . . × a [ r − 1 ] × a [ r ] t=a[l]×a[l+1]×...×a[r−1]×a[r] t=a[l]×a[l+1]×...×a[r1]×a[r],求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;
        }

    }
}

以上是关于牛客练习赛85 数学家的迷题 (带修莫队/线段树)的主要内容,如果未能解决你的问题,请参考以下文章

牛客练习赛D数学家的谜题线段树+bitset优化

数学家的迷题(bitset+线段树)

牛客练习赛85 A~D题题解

bzoj4129 Haruna’s Breakfast 树上带修莫队+分块

[国家集训队]数颜色

bzoj 2120 数颜色 (带修莫队)