• 【倍增】LCM QUERY


    给一个序列,每次给一个长度l,问长度为l的区间中lcm最小的。

    题解:因为ai<60,所以以某个点为左端点的区间的lcm只有最多60种的情况,而且相同的lcm区间的连续的。

    所以就想到一个n*60*logn的做法,倍增找出每个点的区间lcm情况,然后修改答案……

    1-60的lcm的积大于long long,只能把数拆开,然后比较时用log,结果才用这个数的质因数相乘。

    问题在于一开始我对于每个点开个20的数组记录60内第几个质数的个数,这样每次常数就要再乘个20,然后就tle……

    优化的方法是位运算,因为只会是2,3,5,7的次幂大于1次,单独记录,其他的只会是0次幂和1次幂。

    最后作死的两个小错误:33不是质数……,用ln【60】数组记录log的值然而其中对n取对数……

    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define maxn 40000
    #define mm 1000000007
    #define LL long long
    #define inf 100000000000
    #define rep(i,l,r) for(int i=l;i<=r;i++)
    #define dow(i,l,r) for(int i=r;i>=l;i--)
    using namespace std;
    
    typedef struct {
        int v1;
        double v2;
    }Big;
    Big tree[maxn*4];
    double ln[maxn];
    int f[maxn][17],now;
    int n,m,maxln,tot=24;
    LL ans1[maxn],ans2[maxn];
    int pri[25]={2,4,8,16,
                32,3,9,27,
                5,25,7,49,
                11,13,17,19,
                23,29,31,37,
                41,43,47,53,59};
    
    double calc(int x)
    {
        double sum=0;
        rep(i,12,tot)
            if ((x>>i)&1)
                sum+=ln[pri[i]];
        dow(i,0,4)
            if ((x>>i)&1) {
                sum+=(i+1)*ln[2];
                break;
            }
        if ((x>>7)&1) sum+=3*ln[3];
        else
        if ((x>>6)&1) sum+=2*ln[3];
        else
        if ((x>>5)&1) sum+=ln[3];
        
        if ((x>>9)&1) sum+=2*ln[5];
        else
        if ((x>>8)&1) sum+=ln[5];
        
        if ((x>>11)&1) sum+=2*ln[7];
        else
        if ((x>>10)&1) sum+=ln[7];
        
        return sum;
    }
    
    LL answer(Big x)
    {
        LL sum=1;
        rep(i,12,tot)
            if ((x.v1>>i)&1)
                sum=sum*pri[i]%mm;
        dow(i,0,4)
            if ((x.v1>>i)&1) {
                sum=sum*pri[i]%mm;
                break;
            }
        if ((x.v1>>7)&1) sum=sum*27%mm;
        else
        if ((x.v1>>6)&1) sum=sum*9%mm;
        else
        if ((x.v1>>5)&1) sum=sum*3%mm;
        
        if ((x.v1>>9)&1) sum=sum*25%mm;
        else
        if ((x.v1>>8)&1) sum=sum*5%mm;
        
        if ((x.v1>>11)&1) sum=sum*49%mm;
        else
        if ((x.v1>>10)&1) sum=sum*7%mm;
        
        return sum;
    }
    
    void build(int x,int l,int r)
    {
        tree[x].v2=inf;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
    }
    
    void change(int x,int l,int r,int ll,int rr,double z)
    {
        if (tree[x].v2<=z) return;
        if (ll<=l && r<=rr) {
            tree[x].v1=now;
            tree[x].v2=z;
            return;
        }
        int mid=(l+r)>>1;
        if (ll<=mid) change(x<<1,l,mid,ll,rr,z);
        if (rr>mid) change(x<<1|1,mid+1,r,ll,rr,z);
    }
    
    Big ask(int x,int l,int r,int y)
    {
        if (l==r) return tree[x];
        int mid=(l+r)>>1;
        Big more;
        if (y<=mid) more=ask(x<<1,l,mid,y);
        else more=ask(x<<1|1,mid+1,r,y);
        if (more.v2<tree[x].v2) return more;
        return tree[x];
    }
    
    int main()
    {
        rep(i,1,maxn-1) ln[i]=log(i);
        int tt=0;
        while (scanf("%d %d",&n,&m)!=EOF) {
            ++tt;
            rep(i,1,n) {
                f[i][0]=0;
                int k;
                scanf("%d",&k);
                dow(j,0,tot)
                    if (k%pri[j]==0) k/=pri[j],f[i][0]+=1<<j;
            }
            maxln=floor(ln[n]/ln[2])+1;
            rep(i,1,maxln)
                rep(j,1,n+1-(1<<i)) 
                    f[j][i]=f[j][i-1]|f[j+(1<<(i-1))][i-1];
            build(1,1,n);
            rep(i,1,n) {
                int l=i;
                now=0;
                while (l<=n) {
                //    printf("!!%d %d
    ",now,f[l][0]);
                    now=now|f[l][0]; 
                    int r=l;
                    dow(j,1,maxln) 
                        if (r-1+(1<<j)<=n && !(~((~f[r][j])|now)) ) r=r-1+(1<<j);
                    
                    change(1,1,n,l-i+1,r-i+1,calc(now));
                    l=r+1;
                }
            }
            
            while (m--) {
                int j;
                scanf("%d",&j);
                printf("%lld
    ",answer(ask(1,1,n,j)));
            }
        }
        return 0;
    }
    View Code

    这种区间答案连续的思想并不是第一次遇见了

  • 相关阅读:
    C# 从服务器下载文件
    不能使用联机NuGet 程序包
    NPOI之Excel——合并单元格、设置样式、输入公式
    jquery hover事件中 fadeIn和fadeOut 效果不能及时停止
    UVA 10519 !! Really Strange !!
    UVA 10359 Tiling
    UVA 10940 Throwing cards away II
    UVA 10079 Pizze Cutting
    UVA 763 Fibinary Numbers
    UVA 10229 Modular Fibonacci
  • 原文地址:https://www.cnblogs.com/Macaulish/p/6648198.html
Copyright © 2020-2023  润新知