BZOJ4373: 算术天才⑨与等差数列 线段树

题意:一个序列,两种操作:1.单点修改 2.查询[l,r]内的数由小到大排序后能否形成公差k的等差数列
1<=n,m<=300000,0<=a[i]<=10^9,强制在线
正解是这么说的。。。若能形成公差为k的等差数列则:1.最大值=最小值+(r-l)*k ,2.相邻两数之差都为k的倍数 ,3.没有重复元素
第一个好维护,第二个差分后维护,由于公共gcd从下到上是单调不增的所以均摊log,关键在于第三个。
用set求出每个元素i后面第一个和它相等的元素nex[i],判断[l,r]中的最小nex是否>r即可。
然而注意公差为0要特判,上面这种“不能有重复元素”的判断方法就挂了。。。
l==r也要特判。
然而还有单点修改,第一个第二个也好改,为了维护第三个,每个元素除了记录nex,还要记录前面最近的相等元素pre,修改时像双向链表那样pre->nex=nex,nex->pre=pre就好,新的pre->nex=nex->pre=this。这个过程中涉及到的点都要去线段树里更新信息。
这题还有一种哈希做法。差分后实际上就是比较一个集合是否是k,2k,3k…(n-1)k,分别维护一段区间的和与平方和(由于很大所以需要取模),比较是否与预期相等即可。
平方和公式:这里写图片描述,由于有/6,维护时直接乘以6,就可以该取模取模了。
我写的是第一种。

#ifdef ONLINE_JUDGE
#define NDEBUG
#define null_type null_mapped_type
#endif
#include<cstdio>
#include<ext/pb_ds/assoc_container.hpp>
#define gm 1<<20
using namespace __gnu_pbds;
typedef std::pair<int,int> pr;
typedef tree<pr,null_type> set;
set s;
inline int __gcd(int a,int b)
{
    int c;
    while(b) c=a,a=b,b=c%b;
    return a;
}
int n,m;
int gcd[gm],las[gm],minn[gm],maxx[gm];
inline int max(int a,int b){return a<b?b:a;}
inline int min(int a,int b){return a<b?a:b;}
inline int abs(int x){return x<0?-x:x;}
int val[300002],pre[300002],nex[300002],cf[300002],pos[300002];
void segtree(int l=1,int r=n,int o=1)
{
    if(l==r)
    {
        gcd[o]=cf[l];
        las[o]=nex[l];
        minn[o]=maxx[o]=val[l];
        pos[l]=o;
        return;
    }
    int mid=l+r>>1,ls=o<<1,rs=ls|1;
    segtree(l,mid,ls);
    segtree(mid+1,r,rs);
    gcd[o]=__gcd(gcd[ls],gcd[rs]);
    las[o]=min(las[ls],las[rs]);
    minn[o]=min(minn[ls],minn[rs]);
    maxx[o]=max(maxx[ls],maxx[rs]);
}
inline void modify(int x)
{
    int o=pos[x],ls,rs;
    gcd[o]=cf[x],minn[o]=maxx[o]=val[x],las[o]=nex[x];
    while(o>>=1)
    {
        ls=o<<1,rs=ls|1;
        gcd[o]=__gcd(gcd[ls],gcd[rs]);
        las[o]=min(las[ls],las[rs]);
        minn[o]=min(minn[ls],minn[rs]);
        maxx[o]=max(maxx[ls],maxx[rs]);
    }
}
inline void modify_cf(int x)
{
    int o=pos[x];
    gcd[o]=cf[x];
    while(o>>=1)
    gcd[o]=__gcd(gcd[o<<1],gcd[(o<<1)|1]);
}
inline void modify_nex(int x)
{
    int o=pos[x];
    las[o]=nex[x];
    while(o>>=1)
    las[o]=min(las[o<<1],las[(o<<1)|1]);
}
inline void change_val(int x,int v)
{
    int l,r;
    s.erase(pr(val[x],x));
    cf[x]+=v-val[x],cf[x+1]+=val[x]-v;
    if(x<n) modify_cf(x+1);
    val[x]=v;
    l=pre[x],r=nex[x];
    nex[l]=r,pre[r]=l;
    if(l) modify_nex(l);
    set::iterator a=s.upper_bound(pr(val[x],x)),b=a;
    --a;
    l=a->first==v?a->second:0,r=b->first==v?b->second:n+1;
    nex[l]=pre[r]=x;
    if(l) modify_nex(l);
    pre[x]=l,nex[x]=r;
    s.insert(pr(val[x],x));
    modify(x);
}
int get_l,get_r;
int get_gcd,get_las,get_minn,get_maxx;
void get(int l=1,int r=n,int o=1)
{
    if(get_l<=l&&r<=get_r)
    {
        get_las=min(las[o],get_las);
        get_minn=min(minn[o],get_minn);
        get_maxx=max(maxx[o],get_maxx);
        return;
    }
    int mid=l+r>>1,ls=o<<1,rs=ls|1;
    if(get_l<=mid) get(l,mid,ls);
    if(mid<get_r) get(mid+1,r,rs);
}
void __get_gcd(int l=1,int r=n,int o=1)
{
    if(get_l<=l&&r<=get_r)
    {
        get_gcd=__gcd(gcd[o],get_gcd);
        return;
    }
    int mid=l+r>>1,ls=o<<1,rs=ls|1;
    if(get_l<=mid) __get_gcd(l,mid,ls);
    if(mid<get_r) __get_gcd(mid+1,r,rs);
}
int cnt=0;
inline const char* check(int x,int y,int k)
{
    if(x==y) return ++cnt,"Yes";
    get_gcd=0,get_las=n+1,get_minn=0x7fffffff,get_maxx=-1;
    get_l=x,get_r=y;
    get();
    if(!k) return get_minn==get_maxx?(++cnt,"Yes"):"No";
    if(get_las<=y) return "No";
    if(get_maxx!=get_minn+(long long)(y-x)*k) return "No";
    get_l=x+1;
    __get_gcd();
    return abs(get_gcd)==k?(++cnt,"Yes"):"No";
}
int op,x,y,k;
int main()
{
    scanf("%d%d",&n,&m);
    s.insert(pr(0,0));
    s.insert(pr(0x7fffffff,n+1));
    for(int i=1;i<=n;++i)
    scanf("%d",val+i),s.insert(pr(val[i],i)),cf[i]=val[i]-val[i-1];
    for(int i=1;i<=n;++i)
    {
        set::iterator a=s.find(pr(val[i],i)),b=a;
        --a;++b;
        pre[i]=a->first==val[i]?a->second:0;
        nex[i]=b->first==val[i]?b->second:n+1;
    }
    segtree();
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&op,&x,&y);
        x^=cnt,y^=cnt;
        if(op==1)
            change_val(x,y);
        else
        {
            scanf("%d",&k);
            k^=cnt;
            puts(check(x,y,k));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值