HDU

来源:互联网 发布:ubuntu更改系统语言 编辑:程序博客网 时间:2024/06/06 00:43

题目链接

题意:

给定一个a数组,每次询问一个区间[l,r]求这个区间内所有子区间的gcd的种类。

思路:

这个题目类似于区间不同数的个数

首先知道gcd的性质,在固定一个端点的情况下,连续区间的gcd的个数最多为log级别的.
我们可以固定每个点作为右端点,然后预处理出以该点为右端点的所有不同gcd的情况,相同gcd的取左端点大的.最后离线处理所有的询问,用树状数组维护一下即可》

#include<bits/stdc++.h>#define pb push_back#define mk make_pairusing namespace std;const int maxn = 1e6+5;typedef long long ll;int n,q;vector<pair<int,int> > vt[maxn];int s[maxn],vis[maxn],ans[maxn],a[maxn];struct node{    int l,r,id;    bool operator <(const node & w) const    {        return r < w.r;    }}op[maxn];int lowbit(int x){    return x & -x;}void add(int x,int d){    while(x < maxn)    {        s[x] += d;        x += lowbit(x);    }}int sum(int x){    int res = 0;    while(x)    {        res += s[x];        x -= lowbit(x);    }    return res;}void init(){    memset(s,0,sizeof s);    memset(vis,0,sizeof vis);    for(int i = 0;i < maxn;++i)    vt[i].clear();}void solve(){    for(int i = 1;i <= n;++i)    {        vt[i].pb(mk(a[i],i));        int pre = a[i];        for(int j = 0;j < vt[i-1].size();++j)        {            int tmp = __gcd(vt[i-1][j].first,a[i]);            if(tmp != pre)            vt[i].pb(mk(tmp,vt[i-1][j].second)),pre = tmp;        }    }    int cur = 0;    for(int i = 1;i <= q;++i)    {        while(cur < op[i].r)        {            cur++;            for(int j = 0;j < vt[cur].size();++j)            {                int tmp = vt[cur][j].first;                int pos = vt[cur][j].second;                if(vis[tmp]) add(vis[tmp],-1);                vis[tmp]=pos;                add(pos,1);            }        }        ans[op[i].id] = sum(op[i].r)-sum(op[i].l-1);    }    for(int i = 1;i <= q;++i)    printf("%d\n",ans[i]);}int main(){    while(~scanf("%d %d",&n,&q))    {        init();         for(int i = 1;i <= n;++i)        scanf("%d",&a[i]);        for(int i = 1;i <= q;++i)        {            scanf("%d %d",&op[i].l,&op[i].r);            op[i].id = i;        }        sort(op+1,op+1+q);        solve();    }    return 0;} 
原创粉丝点击