zoukankan      html  css  js  c++  java
  • dutacm.club_1094_等差区间_(线段树)(RMQ算法)

    1094: 等差区间

    Time Limit:5000/3000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)
    Total Submissions:843   Accepted:89
    [Submit][Status][Discuss]

    Description

    已知一个长度为 nn 的数组 a[1],a[2],,a[n],我们进行 qq 次询问,每次询问区间 a[l],a[l+1],,a[r1],a[r],数字从小到大排列后,是否会形成等差数列。等差数列的定义为,数列相邻两项(后一项减去前一项)的差值相等。

    Input

    本题有多组输入数据。

    每组输入数据第一行输入两个正整数 nn 和 qq。第二行输入 nn 个正整数 a[1],a[2],,a[n]。最后输入 qq 行,每行两个数字 l,rl,r(1lrn),表示询问区间 a[l],,a[r]

    1n,q10^5,1a[i]10^6

    Output

    对于每组询问输出一行,如果形成等差数列,输出“Yes ”,否则输出“No”(不含引号)。

    Sample Input

    5 5
    3 1 5 2 4
    1 3
    4 5
    1 4
    3 4
    2 2
    

    Sample Output

    Yes
    Yes
    No
    Yes
    Yes

    题意:给定一个n位数列,q条查询[l,r],询问子序列[l--r]排序后是否为等差数列。

    看题解说用的什么 RMQ求区间最大最小 但是没有学过,过些天再补。我用线段树来代替的求最大最小值。

    思路:一个区间要是等差数列:1.所有数相等;2.所有数不等,且求公差,满足g*(r-l)==maxn-minn。

    然后就是公差,我反正是没想到。求这个序列所有相邻两项差的最大公约数,结果即为公差,求公差也需要在线段树中进行,不能枚举。

    同时,需要记录当前这个数上一次出现的位置。

    线段树中维护区间:最大值,最小值,公差,序列中所有出现过的数上一次出现的位置的最大值(因为第2中情况需要所有数不同)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<stdlib.h>
    using namespace std;
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    #define N 100005
    
    int num[N];
    
    struct Node
    {
        int l,r;
        int maxn,minn,g,lef;
    } tree[N<<2];
    
    int Gcd(int a,int b)
    {
        if(a==0||b==0)
            return 0;
        if(a<b)
        {
            int t=a;
            a=b;
            b=t;
        }
        if(a%b==0)
            return b;
        return Gcd(b,a%b);
    }
    
    int cha[N],loc[N*10],last[N];
    void build(int l,int r,int rt)
    {
        tree[rt].maxn=tree[rt].minn=0;
        tree[rt].l=l;
        tree[rt].r=r;
        if(l==r)
        {
            tree[rt].maxn=num[l];
            tree[rt].minn=num[l];
            tree[rt].g=-1;
            tree[rt].lef=last[l];
            return;
        }
        int mid=(l+r)>>1;
        build(lson);
        build(rson);
        tree[rt].maxn=max(tree[rt<<1].maxn,tree[rt<<1|1].maxn);
        tree[rt].minn=min(tree[rt<<1].minn,tree[rt<<1|1].minn);
        if(tree[rt<<1].l==tree[rt<<1].r&&tree[rt<<1|1].l==tree[rt<<1|1].r) //建树时求出所有子节点数大于1的结点的公差
            tree[rt].g=abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].l]);
        else if(tree[rt<<1|1].l==tree[rt<<1|1].r)
            tree[rt].g=Gcd(tree[rt<<1].g,abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].l]));
        else
            tree[rt].g=Gcd(Gcd(tree[rt<<1].g,abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].l])),tree[rt<<1|1].g);
        tree[rt].lef=max(tree[rt<<1].lef,tree[rt<<1|1].lef);
    }
    
    struct Res
    {
        int maxn,minn,g,lef;
        Res(){}
        Res(int a,int b,int g1,int le)
        {
            maxn=a;
            minn=b;
            g=g1;
            lef=le;
        }
    };
    
    /*Res deal(Res a,Res b)
    {
        Res tmp(max(a.maxn,b.maxn),min(a.minn,b.minn));
        return tmp;
    }*/
    
    Res query(int L,int R,int l,int r,int rt)
    {
        if(L==l&&r==R)
        {
            Res tmp(tree[rt].maxn,tree[rt].minn,tree[rt].g,tree[rt].lef);
            return tmp;
        }
        int mid=(l+r)>>1;
        if(L>mid)
            return query(L,R,rson);
        else if(R<=mid)
            return query(L,R,lson);
        else
        {
            Res r1=query(L,mid,lson);
            Res r2=query(mid+1,R,rson);
            Res r3;
            r3.maxn=max(r1.maxn,r2.maxn);
            r3.minn=min(r1.minn,r2.minn);
            if(r1.g==-1&&r2.g==-1)  //查询时需注意,若查到叶子结点,其公约数为-1,需特殊处理
                r3.g=abs(num[mid]-num[mid+1]);
            else if(r1.g==-1)
                r3.g=Gcd(abs(num[mid]-num[mid+1]),r2.g);
            else if(r2.g==-1)
                r3.g=Gcd(abs(num[mid]-num[mid+1]),r1.g);
            else
                r3.g=Gcd(r1.g,Gcd(abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].l]),r2.g));
            r3.lef=max(r1.lef,r2.lef);
            return r3;
        }
    }
    
    
    
    int main()
    {
        int n,q;
        while(scanf("%d%d",&n,&q)!=EOF)
        {
            //memset(tree,0,sizeof(tree));
            memset(loc,0,sizeof(loc));
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&num[i]);
                if(loc[num[i]]==0)
                    last[i]=0;
                else
                    last[i]=loc[num[i]];
                loc[num[i]]=i;
                if(i>1)
                    cha[i-1]=abs(num[i]-num[i-1]);
            }
    
            build(1,n,1);
            while(q--)
            {
                int ll,rr;
                scanf("%d%d",&ll,&rr);
                Res tm=query(ll,rr,1,n,1);
                int maxn=tm.maxn;
                int minn=tm.minn;
                int g=tm.g;
                int lef=tm.lef;
                //cout<<maxn<<' '<<minn<<' '<<g<<' '<<lef<<endl;
                if(minn==maxn)
                {
                    printf("Yes
    ");
                    continue;
                }
                if(lef<ll&&(g*(rr-ll)==maxn-minn))
                    printf("Yes
    ");
                else
                    printf("No
    ");
            }
        }
        return 0;
    }
     RMQ(Range Minimum/Maximum Query),即区间最值查询。一种动态规划。思路和线段树的一样。
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<stdlib.h>
    using namespace std;
    #define N 100005
    
    int dpMax[N][18],dpMin[N][18],dpG[N][18],dpLeft[N][18],n,loc[N*10];
    
    int Gcd(int a,int b)
    {
        if(a==0||b==0)
            return 0;
        if(a<b)
        {
            int tmp=a;
            a=b;
            b=tmp;
        }
        if(a%b==0)
            return b;
        return Gcd(b,a%b);
    }
    
    void RMQ()
    {
        for(int j=1; j<=17; j++)
            for(int i=1; i<=n; i++)
                if((1<<j)+i-1<=n)
                {
                    dpMax[i][j]=max(dpMax[i][j-1],dpMax[i+(1<<j-1)][j-1]);
                    dpMin[i][j]=min(dpMin[i][j-1],dpMin[i+(1<<j-1)][j-1]);
                    dpLeft[i][j]=max(dpLeft[i][j-1],dpLeft[i+(1<<j-1)][j-1]);
                    if(j==1)
                        dpG[i][j]=abs(dpMin[i][0]-dpMin[i+1][0]);
                    else
                        dpG[i][j]=Gcd(dpG[i][j-1],Gcd(abs(dpMin[i+(1<<j-1)][0]-dpMin[i+(1<<j-1)-1][0]),dpG[i+(1<<j-1)][j-1]));
                }
    }
    
    int main()
    {
        //cout<<log2(0)<<endl;
        int q;
        while(scanf("%d%d",&n,&q)!=EOF)
        {
            memset(loc,0,sizeof(loc));
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&dpMin[i][0]);
                dpMax[i][0]=dpMin[i][0];
                dpG[i][0]=-1;
                if(loc[dpMin[i][0]]==0)
                    dpLeft[i][0]=0;
                else
                    dpLeft[i][0]=loc[dpMin[i][0]];
                loc[dpMin[i][0]]=i;
            }
            RMQ();
            while(q--)
            {
                int l,r;
                scanf("%d%d",&l,&r);
                int k=log2(r-l+1);
                int maxn=max(dpMax[l][k],dpMax[r-(1<<k)+1][k]);
                int minn=min(dpMin[l][k],dpMin[r-(1<<k)+1][k]);
                int left=max(dpLeft[l][k],dpLeft[r-(1<<k)+1][k]);
                int g=abs(dpMin[l][0]-dpMin[r][0]);
                if(r-l>1)
                    g=Gcd(g,Gcd(dpG[l][k],dpG[r-(1<<k)+1][k]));
                //cout<<maxn<<' '<<minn<<' '<<g<<' '<<left<<endl;
                if(maxn==minn)
                    printf("Yes
    ");
                else
                {
                    if(left<l&&maxn-minn==(r-l)*g)
                        printf("Yes
    ");
                    else
                        printf("No
    ");
                }
            }
        }
        return 0;
    }
     
  • 相关阅读:
    xcode 定义自己的代码片段
    iOS 开发技巧
    iOS 上传自己的库到cocoapod
    制作正式版10.11 OS X El Capitan 安装U盘(优盘)
    查看 共享内存 的命令 ipcrm、ipcs
    批量kill 进程
    GROUP BY、HAVING、AS 的用法小例子
    C++多线程中调用python api函数
    C++ 查询某个变量的类型
    C++ 把枚举变量的名称,直接当字符串使用方法 字符串化符号 #
  • 原文地址:https://www.cnblogs.com/jasonlixuetao/p/6556396.html
Copyright © 2011-2022 走看看