HDU5726

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5726


第一次打多校,没有什么经验,果断被虐,学长说多校切4题区域赛一般拿牌,比赛前感觉出题应该没什么问题,然而……一上午都在和老师讨论项目的事,等结束没有休息就切题,感觉好累
A题扔给队友,榜单07题5发5A,看了一下没什么思路,看题目有GCD,前几天刚训练数论专题,果断看了GCD,坑呀……简介一下吧


题意大概就是给你n个数(n<100000)q次查询(q<100000)首先问gcd(a[i…….j]),然后有多少对gcd(a[in……jn])与gcd(a[i…….j])相等.
前几天刚写过相似的题目,利用dp[i][j]去写,dp前i个数的gcd为j,如果此题ai较小的话可以去写,但是……那么就换方法,比赛时也没想到什么,比赛结束看标称才有点感悟(毕竟太菜)
第一问较简单,利用线段树求gcd较为简单(学了好久也忘了刚好熟悉一下)第二问就是先预处理出以i为开头的区间的各自的gcd值,用线段树维护区间gcd和查询,然后询问的时候直接输出即可。


/********************
    Acm ID: OffensiveChild
    Author: wzh
    proverbs:yiroad keep straight on
    Tittle:线段树求解区间GCD
    Time:2016.07.20
*********************/
#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
using namespace std;
const int maxm=1000+5;
const int maxn=100000+5;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch =='-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
int n;
int num[maxn];
int gcd(int a, int b) {
    while(b) {
        int r = a % b;
        a = b;
        b = r;
    }
    return a;
}
struct segtree
{
    int g[4*maxn];
    void build(int i,int l,int r)
    {
        if(l==r)
        {
            g[i]=num[l];
            return;
        }
        int mid=(l+r)>>1;
        build(2*i,l,mid);
        build(2*i+1,mid+1,r);
        g[i]=gcd(g[2*i],g[2*i+1]);
    }
    int query(int i,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R)return g[i];
        int mid=(l+r)>>1;
        int ls=0,rs=0;
        if(L<=mid)ls=query(2*i,l,mid, L,R);
        if(R>mid)rs=query(2*i+1,mid+1,r,L,R);
        if(!ls)swap(ls,rs);
        return gcd(ls,rs);
    }
    int search(int i,int l,int r,int L,int R,int cur,int &GCD)
    {
        if(L<=l&&r<=R)
        {
            if(gcd(g[i],GCD)<cur)
            {
                if(l==r)return l;
                else
                {
                    int mid=(l+r)>>1,ls=0,rs=0;
                    ls=search(2*i,l,mid,L,R,cur,GCD);
                    if(ls)return ls;
                    rs=search(2*i+1,mid+1,r,L,R,cur,GCD);
                    return rs;
                }
            }
            else
            {
                GCD=gcd(g[i],GCD);
                return 0;
            }
        }
        int mid=(l+r)>>1,ls=0 ,rs=0;
        if(L<=mid)ls=search(2*i,l,mid,L,R,cur,GCD);
        if(ls)return ls;
        if(R>mid)rs=search(2*i+1,mid+1,r,L,R,cur,GCD);
        return rs;
    }
}seg;
map<int ,ll>M;
int main()
{
    int k=1;
    int T;
    T=read();
    while(T--)
    {
        M.clear();
        n=read();
        for(int i=1; i<=n; i++)
        {
            num[i]=read();
        }
        seg.build(1,1,n);
        int GCD,nex;
        for(int i=1;i<=n;i++)
        {
            int g=seg.query(1,1,n,i,n);
            for(int now=i,cur=num[i];now<=n;)
            {
                if(g==cur){M[g]+=n-now+1;break;}
                GCD=num[i];
                nex=seg.search(1,1,n,i,n,cur,GCD);
                M[cur]+=nex-now;
                now=nex;
                cur=gcd(num[now],cur);
            }
        }
        int q;
        q=read();
        printf("Case #%d:\n",k++);
        int a,b,ans;
        for(int i=1;i<=q;i++)
        {
            a=read();
            b=read();
            ans=seg.query(1,1,n,a,b);
            printf("%d %I64d\n",ans,M[ans]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值