• 51nod 1203 jzplcm


    长度为N的正整数序列S,有Q次询问,每次询问一段区间内所有数的lcm(即最小公倍数)。由于答案可能很大,输出答案Mod 10^9 + 7。

     
    例如:2 3 4 5,询问[1,3]区间的最小公倍数为2 3 4的最小公倍数 = 12。
    Input
    第1行:两个整数,N, Q,中间用空格分隔,N为数列长度,Q为询问数量。(2 <= N, Q <= 50000)
    第2 - N + 1行:每行1个整数,对应数列中的元素(1 <= S[i] <= 50000)
    第N + 2 - N + Q + 1行:每行2个数,l, r,表示询问下标i在[l, r]范围内的S[i]的最小公倍数。(1 <= l <= r <= N)
    Output
    输出共Q行,对应询问区间的最小公倍数Mod 10^9 + 7。
    Input示例
    3 3
    123
    234
    345
    1 2
    2 3
    1 3
    Output示例
    9594
    26910
    1103310


    由于ai很小可以用各种方法乱搞,但这是一道论文题,原题的ai有1e9;
    我们考虑这种有关lcm和gcd的题一种常用的处理方法就是分解质因数,这个题我们相当于是要求每个质因子的幂的最大值;
    这个就很皮了,因为区间中不同质因子的数量是可能很多的,一个个枚举质因子肯定是假的;
    我们可以考虑干这样一个骚操作,我们分解质因数的时候,我们就真的把他分解,比如说这个数有p^q,
    那么我们就拆为p^1,p^2,p^3,...,p^q,这么多个数,然后每个数的权值为p,然后问题转化为区间中不同的数的乘积;
    这样显然是对的,因为我们假设p这个质因子的最大次幂为q,那么会有p这个数会乘q次,因为有p^1,p^2,p^3...p^q每次都乘了p,满足lcm的定义;
    于是我们发现这是一个经典的问题,对于每个数我们肯定是在他第一次出现在区间中的时候计算贡献,那么我们用类似HH的项链和采花的套路,用一个la[i],表示i位置上的数上一次出现的位置;
    于是我们变为了询问[l,r]中la[i]<l的数的乘积,我们把询问按照右端点排序用树状数组维护前缀乘积即可,具体实现方法和采花类似;
    论文链接里面还有各种做法,以及题目的分析过程
    //MADE BY QT666
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int N=300050;
    const int Mod=1e9+7;
    int n,q,la[N],prime[N],tot,vis[N],a[N],b[N*20],v[N*20],tt,st[N],ed[N];
    vector<int> p[N];
    void pre(){
        for(int i=2;i<=100000;i++){
    	if(!vis[i]) prime[++tot]=i;
    	for(int j=i;j<=100000;j+=i) vis[j]=1;
        }
        for(int i=1;i<=tot;i++){
    	for(int j=prime[i];j<=100000;j+=prime[i]) p[j].push_back(prime[i]);
        }
    }
    struct data{
        int l,r,id;
    }Q[N];
    int last[N];
    bool cmp(const data &a,const data &b){
        return a.r<b.r;
    }
    ll tr[N*20],ans[N];
    int lowbit(int x){return x&-x;}
    void update(int x,ll v){ 
        if(x==0) return;
        for(int i=x;i<=tt;i+=lowbit(i)) (tr[i]*=v)%=Mod;
    }
    ll query(int x){
        ll ret=1;
        for(int i=x;i;i-=lowbit(i)){
    	(ret*=1ll*tr[i])%=Mod;
        }
        return ret;
    }
    ll qpow(ll x,ll y){
        ll ret=1;
        while(y){
    	if(y&1) (ret*=x)%=Mod;
    	(x*=x)%=Mod;y>>=1;
        }
        return ret;
    }
    int main(){
        scanf("%d%d",&n,&q);pre();
        for(int i=1;i<=n;i++){
    	scanf("%d",&a[i]);
    	int x=a[i];st[i]=tt+1;
    	for(int j=0;j<p[a[i]].size();j++){
    	    int y=p[a[i]][j],z=p[a[i]][j];
    	    while(x%y==0){
    		b[++tt]=z;v[tt]=y;x/=y;z*=y;
    	    }
    	}
    	ed[i]=tt;
        }
        for(int i=1;i<=q;i++){
    	scanf("%d%d",&Q[i].l,&Q[i].r);
            Q[i].l=st[Q[i].l],Q[i].r=ed[Q[i].r],Q[i].id=i;
        }
        for(int i=1;i<=tt;i++) la[i]=last[b[i]],last[b[i]]=i;
        sort(Q+1,Q+1+q,cmp);int l=1;
        tr[0]=1;
        for(int i=1;i<=tt;i++) tr[i]=1;
        for(int i=1;i<=q;i++){
    	while(l<=Q[i].r){
    	    update(la[l],qpow(v[l],Mod-2)),update(l,v[l]);l++;
    	}
    	ans[Q[i].id]=query(Q[i].r)*qpow(query(Q[i].l-1),Mod-2)%Mod;
        }
        for(int i=1;i<=q;i++) printf("%lld
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    HDU 2874 Connections between cities(LCA离线算法实现)
    LCA离线算法Tarjan详解
    HDU 2586 How far away ?(LCA在线算法实现)
    LCA在线算法详解
    LA 4287 等价性证明(强连通分量缩点)
    POJ 2117 Electricity(割点求连通分量)
    ZOJ 1015 Fishing Net(弦图判定)
    BZOJ 1006: [HNOI2008]神奇的国度(弦图染色)
    POJ 2976 Dropping tests(分数规划)
    BZOJ 1003: [ZJOI2006]物流运输(spfa+dp)
  • 原文地址:https://www.cnblogs.com/qt666/p/7612563.html
Copyright © 2020-2023  润新知