zoukankan      html  css  js  c++  java
  • 【倍增】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

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

  • 相关阅读:
    【转】win8.1下安装ubuntu
    Codeforces 1025G Company Acquisitions (概率期望)
    Codeforces 997D Cycles in Product (点分治、DP计数)
    Codeforces 997E Good Subsegments (线段树)
    Codeforces 1188E Problem from Red Panda (计数)
    Codeforces 1284E New Year and Castle Building (计算几何)
    Codeforces 1322D Reality Show (DP)
    AtCoder AGC043C Giant Graph (图论、SG函数、FWT)
    Codeforces 1305F Kuroni and the Punishment (随机化)
    AtCoder AGC022E Median Replace (字符串、自动机、贪心、计数)
  • 原文地址:https://www.cnblogs.com/Macaulish/p/6648198.html
Copyright © 2011-2022 走看看