【线段树】“美登杯”上海市高校大学生程序设计邀请赛(华东理工大学)E. 小花梨的数组

题目链接https://acm.ecnu.edu.cn/contest/173/problem/E/


题意:

在一个数组上有三种操作

  1. 区间乘最小素因子
  2. 区间除最小素因子
  3. 查询单点值

题解:

因为如果先乘后除就会抵消,所以最后一定是:除除除除乘乘乘乘,
维护一个线段树的lazy标记,记录每一个区间的除法个数和乘法个数。做除法的时候,先在乘法个数里面减,然后有剩余在除法个数里面加。做乘法就直接往乘法个数里加。
因为有pushdown操作的存在,所以保证了顺序是先做子区间标记的除和乘,再做父区间标记的除和乘。
还有就是做质因数分解的时候,要随时判断素数来优化。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+7;
const ll mod=1e9+7;
ll n;
ll d[N<<2],t[N<<2];
ll a[N];
ll pri[N],tot;
bool vis[N];
void init(){
    memset(vis,false,sizeof(vis));
    for(ll i=2;i<N;i++){
        if(!vis[i]) pri[++tot]=i;
        for(ll j=1;j<=tot&&i*pri[j]<N;j++){
            vis[i*pri[j]]=true;
            if(i%pri[j]==0) break;
        }
    }
}
void upd(ll rt,ll p){
    ll dd=d[p],tt=t[p];
    if(t[rt]<=dd) dd-=t[rt],t[rt]=0;
    else t[rt]-=dd,dd=0;
    d[rt]+=dd;
    t[rt]+=tt;
}
void pd(ll rt){
    upd(rt<<1,rt);
    upd(rt<<1|1,rt);
    d[rt]=t[rt]=0;
}
void tim(ll rt,ll l,ll r,ll L,ll R){
    if(L<=l&&r<=R){
        t[rt]++;
        return;
    }
    pd(rt);
    ll m=l+r>>1;
    if(L<=m) tim(rt<<1,l,m,L,R);
    if(m<R) tim(rt<<1|1,m+1,r,L,R);
}
void div(ll rt,ll l,ll r,ll L,ll R){
    if(L<=l&&r<=R){
        if(t[rt]) t[rt]--;
        else d[rt]++;
        return;
    }
    pd(rt);
    ll m=l+r>>1;
    if(L<=m) div(rt<<1,l,m,L,R);
    if(m<R) div(rt<<1|1,m+1,r,L,R);
}

ll que(ll rt,ll l,ll r,ll x){
    if(l==r) return rt;
    pd(rt);
    ll m=l+r>>1;
    if(x<=m) return que(rt<<1,l,m,x);
    else return que(rt<<1|1,m+1,r,x);
}
ll qpow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        b>>=1;a=a*a%mod;
    }
    return res;
}
int main(){
    init();
   // printf("%lld\n",tot);
    ll n,m;
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }

    ll o,x,y;
    while(m--){
        scanf("%lld",&o);
        if(o==1){
            scanf("%lld%lld",&x,&y);
            tim(1,1,n,x,y);
        }
        else if(o==2){
            scanf("%lld%lld",&x,&y);
            div(1,1,n,x,y);
        }
        else{
            scanf("%lld",&x);
            ll r=que(1,1,n,x);
            ll ans=a[x];

            for(ll i=1,j=1; ans>1 && i<=tot && j<=d[r]; i++){
                if(!vis[ans]){
                    ans=1;
                    break;
                }
                while((j<=d[r]) && (ans%pri[i]==0)){
                    ans/=pri[i];
                    j++;
                }

            }

            if(!vis[ans]) ans=ans*qpow(ans,t[r])%mod;
            else{
                for(ll i=1;i<=tot;i++){
                    if((ans%pri[i])==0){
                        ans=ans*qpow(pri[i],t[r])%mod;
                        break;
                    }
                }
            }

            printf("%lld\n",ans);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值