hdu 5716 GCD

一句话题面,询问区间GCD和区间GCD等于所询问的区间GCD的区间个数

数的个数1e5,数的大小1e9


区间GCD满足区间加法,所以线段树搞搞就好了


关于第二个询问,需要实现预处理GCD 为x的区间的个数

具体的,考虑固定左端点的情况,不难发现gcd是随着区间长度的增加而递减的,形象的来说就是一些线段(不妨设为(g,length),其中g代表的是左端点固定时候右端点在这个线段中时的区间gcd,length代表这个线段的长度),需要支持的操作是取出线段,更新线段的g,合并g相同的线段

考虑从右往左枚举左端点,不妨设新加入的左端点的值为v,对于现在的每一个线段的g都更新为 gcd(g,v),并且另外加入一个(v,1)的线段,合并g相同的线段


大概就是这样,具体见代码

#include<bits/stdc++.h>
using namespace std;

#define LL long long 

const int maxn = 112345;

int arr[maxn];

map<int,LL> M;
map<int,int> seg,tseg;


void init(int n){
    M.clear();
    seg.clear(),tseg.clear();
    for(int st = n;st >= 1;st--){
        tseg.clear();
        for(auto x : seg){
            int gcd = x.first;
            tseg[__gcd(arr[st],gcd)]+=x.second;
        }
        seg = tseg;
        seg[arr[st]]++; 
        for(auto x : seg){
            M[x.first] += x.second;
        }
    }
}

int info[maxn*4];

#define root 1,1,n
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define Now int o,int l,int r
#define Mid int m = (l + r) / 2;

void init(Now){
    if(l == r){
        info[o] = arr[l];
        return;
    }
    Mid;
    init(lson),init(rson);
    info[o] = __gcd(info[o<<1],info[o<<1|1]);
}

int ql,qr,n;

int query(Now){
    if(ql <= l && r <= qr){
        return info[o];
    }
    Mid;
    int al = -1,ar = -1;
    if(ql <= m) al = query(lson);
    if(m+1<=qr) ar = query(rson);
    if(al == -1) return ar;
    if(ar == -1) return al;
    return __gcd(al,ar);
}

int query(int l,int r){
    ql = l,qr = r;
    return query(root);
}
int main(){
    int T,icase = 1;
    scanf("%d",&T);
    while(T-- && ~scanf("%d",&n)){
        for(int i=1;i<=n;i++){
            scanf("%d",&arr[i]);
        }
        init(n);
        init(root);
        int m,l,r;
        printf("Case #%d:",icase++);
        scanf("%d",&m);
        while(m--){
            scanf("%d %d",&l,&r);
            int ans = query(l,r);
            printf("%d %I64d\n",ans,M[ans]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值