LG5278 算术天才⑨与等差数列 和 CH4302 Interval GCD

算术天才⑨与等差数列

给出一个长度为 N 的序列,需要支持以下两个操作:

  1. 单点修改
  2. 询问每个区间内数字升序排列后是否为等差数列·

N ≤ 100000, M ≤ 200000, datai ≤ 109

题解

若想知道区间内数是否能够成等差数列,显然要求出区间内的最值(即数列首项和末项),则即可算出对应的公差。

注意到若区间内数能成等差数列,则需满足下列中的一项:

  1. 公差为零;
  2. 公差不为零,则数列内无相同元素,那么公差为相邻的数值差的最大公因数。

于是需要维护,区间最大值最小值,差分数列的区间 gcd,以及每个数前驱相同元素的位置。维护每个数前驱相同元素的位置可以用 map 套 set。

可能是错的?

维护区间最大值,最小值,和,平方和,然后来判断。

等差数列平方和公式暴力展开就行了。

有人说能卡掉?

typedef __int128 LL;

CO int N=300000+10;
struct node{
    int min,max;
    LL sum1,sum2;
};
node operator+(CO node&a,CO node&b){
    return (node){
        min(a.min,b.min),max(a.max,b.max),
        a.sum1+b.sum1,a.sum2+b.sum2
    };
}

node tree[4*N];
#define lc (x<<1)
#define rc (x<<1|1)
void build(int x,int l,int r){
    if(l==r){
        int v=read<int>();
        tree[x]=(node){v,v,v,(LL)v*v};
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid),build(rc,mid+1,r);
    tree[x]=tree[lc]+tree[rc];
}
void change(int x,int l,int r,int p,int v){
    if(l==r){
        tree[x]=(node){v,v,v,(LL)v*v};
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) change(lc,l,mid,p,v);
    else change(rc,mid+1,r,p,v);
    tree[x]=tree[lc]+tree[rc];
}
node query(int x,int l,int r,int ql,int qr){
    if(ql<=l and r<=qr) return tree[x];
    int mid=(l+r)>>1;
    if(qr<=mid) return query(lc,l,mid,ql,qr);
    if(ql>mid) return query(rc,mid+1,r,ql,qr);
    return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}

int main(){
    int n=read<int>(),m=read<int>();
    build(1,1,n);
    int yes=0;
    while(m--){
        if(read<int>()==1){
            int p=read<int>()^yes,v=read<int>()^yes;
            change(1,1,n,p,v);
        }
        else{
            int l=read<int>()^yes,r=read<int>()^yes,k=read<int>()^yes;
            bool valid=1;
            node x=query(1,1,n,l,r);
            if(x.max-x.min!=(LL)(r-l)*k) valid=0;
            if(x.sum1!=(LL)(x.min+x.max)*(r-l+1)/2) valid=0;
            if(x.sum2!=(LL)(r-l+1)*x.min*x.min+(LL)2*k*(r-l)*(r-l+1)/2*x.min
            +(LL)k*k*(r-l)*(r-l+1)*(2*r-2*l+1)/6) valid=0;
            puts(valid?"Yes":"No"),yes+=valid;
        }
    }
    return 0;
}

描述

给定一个长度为N的数列A,以及M条指令 (N≤5*10^5, M<=10^5),每条指令可能是以下两种之一:
“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。
“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。

输入格式

第一行两个整数N,M,第二行N个整数Ai,接下来M行每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

样例输入

5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4

样例输出

1
2
4

数据范围与约定

  • N,M≤2*10^5,l<=r,数据保证任何时刻序列中的数都是不超过2^62-1的正整数。

分析

根据“更相减损术”,\(gcd(x,y,z)=gcd(x,y-x,z-y)\),所以
\[ \gcd_{i=l}^r A[l] = \gcd(A[l],\gcd_{i=l+1}^r(A[i]-A[i-1])) \]
所以用线段树维护差分序列的gcd,在线段树上单点修改区间查询;然后是要区间修改单点查询原序列的值,用树状数组维护新差分序列即可。

时间复杂度\(O(M \log N)\)

co int N=5e5+1;
int n,m;
ll a[N],b[N],c[N];
#define lowbit(i) (i&-i)
void add(int p,ll v){
    for(;p<=n;p+=lowbit(p)) c[p]+=v;
}
ll ask(int p){
    ll re=0;
    for(;p;p-=lowbit(p)) re+=c[p];
    return re;
}
ll s[N*4];
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
#define lc (x<<1)
#define rc (x<<1|1)
void build(int x,int l,int r){
    if(l==r) return s[x]=b[l],void();
    int mid=l+r>>1;
    build(lc,l,mid),build(rc,mid+1,r);
    s[x]=gcd(s[lc],s[rc]);
}
void change(int x,int l,int r,int p){
    if(l==r) return s[x]=b[l],void();
    int mid=l+r>>1;
    if(p<=mid) change(lc,l,mid,p);
    else change(rc,mid+1,r,p);
    s[x]=gcd(s[lc],s[rc]);
}
ll query(int x,int l,int r,int ql,int qr){
    if(ql>qr) return 0;
    if(ql<=l&&r<=qr) return s[x];
    int mid=l+r>>1;
    if(qr<=mid) return query(lc,l,mid,ql,qr);
    if(ql>mid) return query(rc,mid+1,r,ql,qr);
    return gcd(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
}
int main(){
//  freopen("CH4302.in","r",stdin),freopen("CH4302.out","w",stdout);
    read(n),read(m);
    for(int i=1;i<=n;++i) b[i]=read(a[i])-a[i-1];
    build(1,1,n); // edit 1: reserve for changing b[1]
    for(int l,r;m--;){
        static char op[2];
        scanf("%s",op),read(l),read(r);
        if(op[0]=='C'){
            ll d=read<ll>(); // edit 2
            b[l]+=d,change(1,1,n,l),add(l,d);
            if(r+1<=n) b[r+1]-=d,change(1,1,n,r+1),add(r+1,-d);
        }
        else printf("%lld\n",llabs(gcd(a[l]+ask(l),query(1,1,n,l+1,r))));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/11738331.html