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\n",gcd(qsum(l),query(1,l+1,r)));
54             else printf("%d\n",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

 

    

转载于:https://www.cnblogs.com/DriverLao/p/9439913.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值