bzoj 4373: 算术天才⑨与等差数列 (线段树||线段树+set)

来源:互联网 发布:紫猫编程学院全套教程 编辑:程序博客网 时间:2024/06/08 06:41

题目描述

传送门

题目大意:
操作1:x,y,将a[x]修改成y
操作2:l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列。

题解1

等差数列求和公式(a1+an)n2
如果单纯的维护区间最小值最大值以及区间和,利用等差数列求和公式计算的话出错的概率会非常高,所以我们再维护一下区间平方和
squ=a21+a22+...+a2n
squ=a21+(a1+k)2+....+(a1+(n1)k)2
squ=na21+(1+4+...+(n1)2)k2+(0+2+...+2(n1))a1k
平方和公式n(n+1)(2n+1)6
平方和可能会炸long long ,所以用unsigned long long 自然溢出即可。

代码1

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#define LL unsigned long long #define N 300013using namespace std;struct data{    LL sum,mn,mx,squ;}tr[N*4];int n,m,a[N];void update(data &now,data l,data r){    now.sum=l.sum+r.sum;    now.squ=l.squ+r.squ;    now.mn=min(l.mn,r.mn);    now.mx=max(l.mx,r.mx);}void build(int  now,int l,int r){    if (l==r) {        tr[now].sum=tr[now].mn=tr[now].mx=(LL)a[l];        tr[now].squ=(LL)a[l]*a[l];        return;    }    int mid=(l+r)/2;    build(now<<1,l,mid);    build(now<<1|1,mid+1,r);    update(tr[now],tr[now<<1],tr[now<<1|1]);}void pointchange(int now,int l,int r,int x,LL v){    if (l==r) {        tr[now].sum=tr[now].mn=tr[now].mx=v;        tr[now].squ=v*v;        return;    }    int mid=(l+r)/2;    if (x<=mid) pointchange(now<<1,l,mid,x,v);    else pointchange(now<<1|1,mid+1,r,x,v);    update(tr[now],tr[now<<1],tr[now<<1|1]);}data query(int now,int l,int r,int ll,int rr){    if (ll<=l&&r<=rr) return tr[now];    int mid=(l+r)/2; bool pd=false;    data ans;    if (ll<=mid) ans=query(now<<1,l,mid,ll,rr),pd=true;    if (rr>mid) {        if (pd) update(ans,ans,query(now<<1|1,mid+1,r,ll,rr));        else ans=query(now<<1|1,mid+1,r,ll,rr);    }    return ans;}int main(){    freopen("a.in","r",stdin);    freopen("my.out","w",stdout);    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++) scanf("%d",&a[i]);    build(1,1,n);    int cnt=0;    for (int i=1;i<=m;i++){        int opt,l,r,x,y,k;        scanf("%d",&opt);        if (opt==1) {            scanf("%d%d",&x,&y);            x^=cnt; y^=cnt;            pointchange(1,1,n,x,y);        }        else {            scanf("%d%d%d",&l,&r,&k);            l^=cnt; r^=cnt; k^=cnt; LL k1=k;            data t=query(1,1,n,l,r);            LL len=(LL)(r-l+1); LL f=(LL)len*(len-1)*(2*len-1)/6;            LL g=(len-1)*len;            LL sum=(LL)(t.mx+t.mn)*len/2;            LL squ=(LL)t.mn*t.mn*len+(LL)k1*k1*f+(LL)g*t.mn*k1;            if (t.sum==sum&&t.squ==squ) printf("Yes\n"),cnt++;            else printf("No\n");        }    }}

题解2

d[i]=abs(a[i]a[i1]),如果区间[l+1,r]中d的最大公约数等于k,且(mxmn)(rl)=k且区间中没有重复的数,那么这个区间可以构成等差序列。
关键是如何判断区间中没有重复的数。
对于每个位置的数x,维护pre表示x前一次出现的位置。[l,r]pre的最大值<l即可。pre的话对于每个权值开一个set,set中维护出现的位置。
注意k=0的时候需要特判一下。

代码2

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<map>#include<set>#define N 300013#define LL long long using namespace std;struct data{    int gcd,pre,mx,mn;    LL sum;}tr[N*4];map<int,int> mp;set<int> T[N*2];int n,m,a[N],b[N],c[N],cnt;int gcd(int x,int y){    if (x==0||y==0) return 0;    int r;    while (y) {        r=x%y;        x=y; y=r;    }    return x;}void update(data &now,data l,data r){    now.gcd=gcd(l.gcd,r.gcd);    now.pre=max(l.pre,r.pre);    now.mn=min(l.mn,r.mn);    now.mx=max(l.mx,r.mx);    now.sum=l.sum+r.sum;}void build(int now,int l,int r){    if (l==r) {        tr[now].pre=b[l]; tr[now].sum=a[l];        tr[now].mx=tr[now].mn=a[l];        tr[now].gcd=c[l];        return;    }    int mid=(l+r)/2;    build(now<<1,l,mid);    build(now<<1|1,mid+1,r);    update(tr[now],tr[now<<1],tr[now<<1|1]);}void change(int now,int l,int r,int x){    if (l==r) {        tr[now].pre=b[l]; tr[now].sum=a[l];        tr[now].mx=tr[now].mn=a[l];        tr[now].gcd=c[l];        return;    }    int mid=(l+r)/2;    if (x<=mid) change(now<<1,l,mid,x);    else change(now<<1|1,mid+1,r,x);    update(tr[now],tr[now<<1],tr[now<<1|1]);}data query(int now,int l,int r,int ll,int rr){    if (ll<=l&&r<=rr) return tr[now];    int mid=(l+r)/2; bool pd=false;    data ans;    if (ll<=mid) ans=query(now<<1,l,mid,ll,rr),pd=true;    if (rr>mid) {        if (pd) update(ans,ans,query(now<<1|1,mid+1,r,ll,rr));        else ans=query(now<<1|1,mid+1,r,ll,rr);    }    return ans;}int main(){    freopen("a.in","r",stdin);//  freopen("my.out","w",stdout);    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++){        scanf("%d",&a[i]);        if (!mp[a[i]]) mp[a[i]]=++cnt,T[cnt].insert(0),T[cnt].insert(N);        T[mp[a[i]]].insert(i);        b[i]=*--T[mp[a[i]]].find(i);    }    for (int i=2;i<=n;i++) c[i]=abs(a[i]-a[i-1]);    build(1,1,n);    int ans=0;    for (int i=1;i<=m;i++){        int opt,x,y,l,r,k;        scanf("%d",&opt);        if (opt==1) {            scanf("%d%d",&x,&y);            x^=ans; y^=ans;            int pre=*--T[mp[a[x]]].find(x);            int nxt=*++T[mp[a[x]]].find(x);            if (nxt!=N)b[nxt]=pre,change(1,1,n,nxt);            T[mp[a[x]]].erase(x);            if (!mp[y]) mp[y]=++cnt,T[cnt].insert(0),T[cnt].insert(N);            T[mp[y]].insert(x);            pre=*--T[mp[y]].find(x); nxt=*++T[mp[y]].find(x);            b[x]=pre; a[x]=y; c[x]=abs(a[x]-a[x-1]); change(1,1,n,x);            b[nxt]=x; change(1,1,n,nxt);            if (x+1<=n) c[x+1]=abs(a[x+1]-a[x]),change(1,1,n,x+1);        }        else {            scanf("%d%d%d",&l,&r,&k);            l^=ans; r^=ans; k^=ans;            data t=query(1,1,n,l,r); data t1=query(1,1,n,l+1,r);            if (k==0) {                if (t.mx==t.mn) printf("Yes\n"),ans++;                else printf("No\n");                continue;            }            if (l==r) {                printf("Yes\n"); ans++;                continue;            }            LL len=r-l+1;            if (t1.gcd==k&&(t.mx-t.mn)/(r-l)==k&&(t.mx-t.mn)%(r-l)==0&&t.pre<l) printf("Yes\n"),ans++;            else printf("No\n");        }    }}