• BZOJ 5028 小Z的加油店


    【题解】

      本题要求求出区间内的各个元素通过加减之后能够得出的最小的数,那么根据裴蜀定理可知答案就是区间内各个元素的最大公约数。

      那么本题题意化简成了维护一个序列,支持区间加上某个数以及查询区间元素的最大公约数。

      我们要证明这样一个定理:

        对于一个序列(a,b,c,d,...),gcd(a,b,c,d,...)=gcd(a,b-a,c-b,d-c,...),文字表述就是原序列的最大公约数等于序列差分后的最大公约数。

      证明方法如下:

          1,设t为序列(a,b,c,d,...)的公约数,a<b<c,b=k1*a+x1,c=k2*a+x2,

        那么我们有t|a,t|b,t|c

        所以有t|x1,t|x2

        所以t|(x2-x1)

        所以t|(k1-1)*a+x1,t|(k2-k1)*a+(x2-x1)

        即t也是序列(a,b-a,c-b,...)的公约数

        同理,(a,b,c,...)的任一公约数也是(a,b-a,c-b,...)的公约数。

          2,设t为序列(a,b-a,c-b,...)的公约数,

        那么有t|a, t|(k1-1)*a+x1, t|(k2-k1)*a+(x2-x1)

        所以有t|x1,t|x2-x1

        所以t|x2

        所以t|a, t|k1*a+x1,t|k2*a+x2

        即t也是序列(a,b,c,...)的公约数

        同理,(a,b-a,c-b,...)的任一公约数也是(a,b,c,...)的公约数。

       综上,gcd(a,b,c,...)=gcd(a,b-a,c-b,...).

      证明完这个定理之后,我们就可以把题意化为求区间第一个元素与后面元素的差分值的GCD

      我们先把原序列差分,线段树维护差分数组的GCD。因为我们已经进行了差分,所以区间加操作变成了点修改,可以直接在线段树上logn完成。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #define LL long long
     5 #define rg register
     6 #define N 100010
     7 #define ls (u<<1)
     8 #define rs (u<<1|1)
     9 #define mid ((a[u].l+a[u].r)>>1)
    10 using namespace std;
    11 int n,m,v[N],c[N],t[N];
    12 struct tree{
    13     int l,r,g;
    14 }a[N<<2];
    15 inline int read(){
    16     int k=0,f=1; char c=getchar();
    17     while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
    18     while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar();
    19     return k*f;
    20 }
    21 int gcd(int x,int y){return y?gcd(y,x%y):x;}
    22 void build(int u,int l,int r){
    23     a[u].l=l; a[u].r=r;
    24     if(l<r) build(ls,l,mid),build(rs,mid+1,r),a[u].g=gcd(a[ls].g,a[rs].g);
    25     else a[u].g=abs(c[l]);
    26 }
    27 void update(int u,int pos,int data){
    28     if(a[u].l==a[u].r){
    29         a[u].g=data; return;
    30     }
    31     update(pos<=mid?ls:rs,pos,data);
    32     a[u].g=gcd(a[ls].g,a[rs].g);
    33 }
    34 int query(int u,int l,int r){
    35     if(l<=a[u].l&&a[u].r<=r) return a[u].g;
    36     int ret=0; bool goleft=0;
    37     if(l<=mid) ret=query(ls,l,r),goleft=1;
    38     if(r>mid){
    39         if(goleft) ret=gcd(ret,query(rs,l,r));
    40         else ret=query(rs,l,r);
    41     } 
    42     return ret;
    43 }
    44 inline void add(int x,int y){for(;x<=n+10;x+=(x&-x)) t[x]+=y;}
    45 inline int qsum(int x){int ret=0; for(;x;x-=(x&-x)) ret+=t[x]; return ret;}
    46 int main(){ 
    47     n=read(); m=read();
    48     for(rg int i=1;i<=n;i++) v[i]=read(),c[i]=v[i]-v[i-1],add(i,c[i]);
    49     build(1,1,n);
    50     while(m--){
    51         int opt=read(),l=read(),r=read(); if(l>r) swap(l,r);
    52         if(opt==1){
    53             if(l<r) printf("%d
    ",gcd(qsum(l),query(1,l+1,r)));
    54             else printf("%d
    ",qsum(l));
    55         }
    56         else{
    57             int del=read();
    58             c[l]+=del; c[r+1]-=del;
    59             add(l,del); if(r<n) add(r+1,-del);
    60             update(1,l,abs(c[l])); if(r<n) update(1,r+1,abs(c[r+1]));
    61         }
    62     }
    63     return 0;
    64 }
    View Code

        

  • 相关阅读:
    php
    nginx
    docker
    pyenv 配置python虚拟环境
    [运维笔记] Nginx编译安装
    [运维笔记] Mysql单库备份脚本
    BurpSuite Intruder 4种攻击模式
    java判断一个单向链表是否有环路
    二分查找(递归和非递归)
    反转链表算法题
  • 原文地址:https://www.cnblogs.com/DriverLao/p/9439913.html
Copyright © 2020-2023  润新知