中国(北方)大学生程序设计训练赛(第一周)-F(线段树)

题目链接:F
首先知道等差数列的一个性质: abs(aiaj)=kd ,所以一个区间的任意两个相邻项之差的绝对值的 gcd 应该是公差 d ,然后通过最大值-最小值也可以确定一个公差,这两个公差相等的话,且区间内无相同元素,则表明是等差数列。特别的,当最大值==最小值也是等差数列。

查询区间内是否有相同元素可以用线段树查询,pre[i] 表示 a[i] 第一次出现的位置,若区间的 max(pre[i])<l ,则无重复元素。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=1e9+7;
const int maxn=1e5+7;
int ma[maxn<<2],mi[maxn<<2],gd[maxn<<2],pre[maxn<<2],vis[1000007];
void init()
{
    memset(ma,0,sizeof(ma));
    memset(mi,0,sizeof(mi));
    memset(gd,0,sizeof(gd));
    memset(pre,0,sizeof(pre));
    memset(vis,0,sizeof(vis));
}
int gcd(int a,int b)
{
    if(b==0)    return a;
    return a%b?gcd(b,a%b):b;
}
int p;
void push_up(int rt)
{
    ma[rt]=max(ma[rt<<1],ma[rt<<1|1]);
    mi[rt]=min(mi[rt<<1],mi[rt<<1|1]);
    gd[rt]=gcd(gd[rt<<1],gd[rt<<1|1]);
    pre[rt]=max(pre[rt<<1],pre[rt<<1|1]);
}
void build(int rt,int l,int r)
{
    if(l==r)
    {
        scanf("%d",&ma[rt]);
        mi[rt]=ma[rt];
        pre[rt]=vis[ma[rt]];
        if(l!=1)    gd[rt]=abs(ma[rt]-p);
        p=ma[rt];
        vis[ma[rt]]=l;
        return ;
    }
    if(l==r)    return ;
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    push_up(rt);
}

int query_max(int rt,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr) return ma[rt];
    int res=-INF;
    int mid=(l+r)>>1;
    if(ql<=mid) res=max(res,query_max(rt<<1,l,mid,ql,qr));
    if(qr>mid)  res=max(res,query_max(rt<<1|1,mid+1,r,ql,qr));
    return res;
}
int query_min(int rt,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr)    return mi[rt];
    int res=INF;
    int mid=(l+r)>>1;
    if(ql<=mid) res=min(res,query_min(rt<<1,l,mid,ql,qr));
    if(qr>mid)  res=min(res,query_min(rt<<1|1,mid+1,r,ql,qr));
    return res;
}
int query_pre(int rt,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr)    return pre[rt];
    int res=-INF;
    int mid=(l+r)>>1;
    if(ql<=mid) res=max(res,query_pre(rt<<1,l,mid,ql,qr));
    if(qr>mid)  res=max(res,query_pre(rt<<1|1,mid+1,r,ql,qr));
    return res;
}
int query_gcd(int rt,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr)    return gd[rt];
    int mid=(l+r)>>1;
    if(qr<=mid) return query_gcd(rt<<1,l,mid,ql,qr);
    if(ql>mid)  return query_gcd(rt<<1|1,mid+1,r,ql,qr);
    return gcd(query_gcd(rt<<1,l,mid,ql,qr),query_gcd(rt<<1|1,mid+1,r,ql,qr));
}
int main()
{
    int n,q;
    while(~scanf("%d%d",&n,&q))
    {
        init();
        int l,r;
        build(1,1,n);
        while(q--)
        {
            scanf("%d%d",&l,&r);
            int low=query_min(1,1,n,l,r);
            int high=query_max(1,1,n,l,r);
            if(low==high||l==r) puts("Yes");
            else if(query_pre(1,1,n,l,r)<l&&1LL*query_gcd(1,1,n,l+1,r)*(r-l)==1LL*(high-low)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值