zoukankan      html  css  js  c++  java
  • SPOJ LGLOVE 7488 LCM GCD Love (区间更新,预处理出LCM(1,2,...,n))

    题目连接:http://www.spoj.com/problems/LGLOVE/

    题意:给出n个初始序列a[1],a[2],...,a[n],b[i]表示LCM(1,2,3,...,a[i]),即1~a[i]的最小公倍数
      然后给出三种操作,注意:0<=i,j<n
      0 i j p :a[i]~a[j]都加上p
      1 i j :求LCM(b[i],b[i+1],...,b[j])
      2 i j :求GCD(b[i],b[i+1],...,b[j])

    思路:
      求LCM(b[i],b[i+1],...,b[j]),也就是求LCM(1,2,...a[i]),LCM(1,2,...a[i+1]),...,LCM(1,2,...a[j]),这些数的最小公倍数
      如果细心的话可以发现,对于LCM(1,2,...,n1),LCM(1,2,...,n2)
      假设n1<n2,那么LCM(1,2,...,n2)必定是LCM(1,2,...,n1)的倍数;反过来,LCM(1,2,...,n1)必定是LCM(1,2,...,n2)的约数。
      因此针对操作1,其实就是求max(a[i],...,a[j]),设为maxval,然后求出LCM(1,2,...,maxval)即可。
      针对操作2,其实就是求min(a[i],...,a[j]),设为minval,然后求出LCM(1,2,...,minval)即可。

      到此,线段树方面的已经解决了,也就是只要存储该区间的最大值和最小值就行了。
      接下来的话,就是如何预处理出LCM(1,2,...,n)了。
      设z[n]=LCM(1,2,...,n),那么我们可以用下面方式来预处理出所有值:
      z[n]=LCM(z[n-1],n)

      LCM(z[n-1],n)=(z[n-1]*n)/GCD(z[n-1],n)
      而如果就按照这个公式这么做的话,由于最后是求模,首先要将除法的求模改成乘逆,即若a=b/c,那么a%mod=b*c^(m-2)%mod,
      然后用到快速幂,再用辗转相除法。。。不用说,也知道,肯定会TLE!!!

      

      再深入想想,n可以化为p1^a1 * p2^a2 * ... pk^ak,p1,p2,...,pk为n的质因数
      从n的质因式分解中取出一个pi,剩下的设为m,注意m<n,那么n=m*pi
      因为z[n-1]=LCM(1,2,...,n-1),而m<=n-1,所以z[n-1]必定是m的倍数,设z[n-1]=a*m这样的话
      即转化为 LCM(z[n-1],n)=LCM(z[n-1],m*pi)=z[n-1]*m*pi/GCD(z[n-1],m*pi)
      而GCD(z[n-1],m*pi)=m*GCD(a,pi)
      所以LCM(z[n-1],n)=z[n-1]*pi/GCD(a,pi)
      1.若pi与a互质,即pi是这么一个数:在1~n-1中不存在pi这么个质因数,或者存在了,但是个数不够,还要乘一次
        z[n]=z[n-1]*pi
      2.若pi与a不互质,即z[n-1]中pi的个数与n中的个数相同,也就是说n中所有的因子都在z[n-1]中了,那么
        z[n]=z[n-1]

      对于每个质因数,设为p,那么p出现第一次,p^2出现第二次,p^3出现第三次...
      那么我们只要在它第i次出现的时候,结果乘上它就行。
      具体操作是和素数筛选法同时进行的,还是见代码吧。

    #include <iostream>
    #include <stdio.h>
    #include <algorithm>
    #include <string.h>
    #define lson rt<<1,L,mid
    #define rson rt<<1|1,mid+1,R
    
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int mod=1000000007;
    const int maxn=100005;
    long long z[maxn*3];  //z[i]=LCM(1,2,...,i)
    int n,m;
    int maxval,minval;
    /*
    预处理z[i]
    z[i]=LCM(1,2,3,...,i)   1<=i<=300000
    */
    void init(){
        memset(z,0,sizeof(z));
        z[0]=0;  //0的话,最小公倍数为0
        z[1]=1;
        for(int i=2;i<maxn*3;i++){
            if(z[i]==0){
                for(int j=2*i;j<maxn*3;j+=i)
                    z[j]=1;
                for(int j=i;j<maxn*3;j*=i)
                    z[j]=i;  //在i^m 的位置都先设为i,到时只要乘上就行
            }
            z[i]*=z[i-1];
            z[i]%mod;
        }
    }
    
    struct Node{
        int maxv,minv; //存储区间的最大值,最小值
        int add;
        int lazy;
    }tree[maxn<<2];
    
    void pushUp(int rt){
        tree[rt].maxv=max(tree[rt<<1].maxv,tree[rt<<1|1].maxv);
        tree[rt].minv=min(tree[rt<<1].minv,tree[rt<<1|1].minv);
    }
    void pushDown(int rt){
        if(tree[rt].lazy){
            tree[rt<<1].add+=tree[rt].add;
            tree[rt<<1|1].add+=tree[rt].add;
            tree[rt<<1].maxv+=tree[rt].add;
            tree[rt<<1].minv+=tree[rt].add;  //一开始漏写了。。。
            tree[rt<<1|1].maxv+=tree[rt].add;  //一开始漏写了。。。
            tree[rt<<1|1].minv+=tree[rt].add;
            tree[rt<<1].lazy=tree[rt<<1|1].lazy=true;
            tree[rt].lazy=false;
            tree[rt].add=0;
        }
    }
    void build(int rt,int L,int R){
        tree[rt].lazy=false;
        tree[rt].add=0;
        if(L==R){
            int v;
            scanf("%d",&v);
            tree[rt].maxv=tree[rt].minv=v;
            return;
        }
        int mid=(L+R)>>1;
        build(lson);
        build(rson);
        pushUp(rt);
    }
    
    void update(int rt,int L,int R,int l,int r,int c){
        if(l<=L&&R<=r){
            tree[rt].maxv+=c;
            tree[rt].minv+=c;
            tree[rt].add+=c;
            tree[rt].lazy=true;
            return;
        }
        int mid=(L+R)>>1;
        pushDown(rt);
        if(l<=mid)
            update(lson,l,r,c);
        if(r>mid)
            update(rson,l,r,c);
        pushUp(rt);
    }
    
    void query(int rt,int L,int R,int l,int r,int op){
        if(l<=L&&R<=r){
            if(op==1){
                maxval=max(maxval,tree[rt].maxv);
            }
            else{
                minval=min(minval,tree[rt].minv);
            }
            return;
        }
        pushDown(rt);
        int mid=(L+R)>>1;
        if(l<=mid)
            query(lson,l,r,op);
        if(r>mid)
            query(rson,l,r,op);
        return;
    }
    int main()
    {
        init();
        int op,a,b,p;
        scanf("%d%d",&n,&m);
        build(1,1,n);
        for(int i=1;i<=m;i++){
            scanf("%d",&op);
            if(op==0){
                scanf("%d%d%d",&a,&b,&p);
                a++;
                b++;
                update(1,1,n,a,b,p);
            }
            else if(op==1){
                maxval=-INF;
                scanf("%d%d",&a,&b);
                a++;b++;
                query(1,1,n,a,b,op);
                if(maxval==0)
                    printf("0
    ");
                else
                    printf("%lld
    ",z[maxval]);
            }
            else{
                minval=INF;
                scanf("%d%d",&a,&b);
                a++;b++;
                query(1,1,n,a,b,op);
                if(minval==0)
                    printf("0
    ");
                else
                    printf("%lld
    ",z[minval]);
            }
        }
        return 0;
    }
  • 相关阅读:
    POJ2299--树状数组求逆序数
    每周总结
    2016湖南省赛--A题--2016
    ACM暑期训练总结
    jQuery实现拖动布局并将排序结果保存到数据库
    TP3.2整合kindeditor
    TP3.2整合uplodify文件上传
    Sublime Text3 使用
    ThinkPHP AJAX分页及JS缓存的应用
    Thinkphp分页类使用
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3426391.html
Copyright © 2011-2022 走看看