【2016-大连赛区网络赛-B】树状数组(Different GCD Subarray Query,hdu 5869)

本文介绍了一种通过预处理和维护线段树来高效解决区间最大公约数(GCD)查询问题的方法。通过按递减顺序预处理每个位置的GCD,并在遍历过程中更新线段树,可以在线性时间内回答所有查询。

http://blog.youkuaiyun.com/saber_acher/article/details/52497104


先对所有i求出它在【1,i】所有子区间的gcd。

这个其实很好求的把a【i】和【1,i-1】所有的gcd比较一下,按递减的顺序push_back,保证不重不漏。

递减是个非常聪明的优化,这样可以避免set。


事先输入所有询问,然后一边遍历所有位子当区间右端点,一边维护线段树,一边回答问题。

突然觉得这个套路好熟悉,最近做的题目多有这种感觉,只不过,有的题目是直接要你求出所有结果,而不是一个个询问,有的题目维护的是单调栈或线段树,而不是树状数组。按顺序枚举,如果枚举中经过了要问的问题,那就直接回答。因此我们只需要维护线段树,使得能回答当前问题,而且今后可以回答后面的问题就可以了,至于已经回答过的问题,就扔掉了,也就不必去保证能回答过去的问题。


在此题中,遍历所有位子当区间右端点。每次先更新新加入的gcd。删除重复的gcd。更新完后,回答所有可以回答的问题。并保存结果。

最后输出。

如果把问题按右端点升序排序,那么寻找可以回答的问题就会十分简单。但是你要保存这个问题是第几个问的,输出的时候就要按顺序输出。


代码

#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
typedef pair<int,int> pii;

int N,Q;
int a[maxn];
vector<pii>vec[maxn];
int tree[maxn];
int vis[10*maxn];
int ANS[maxn];

struct qry
{
    int l,r,id;
    qry(int a,int b,int c):l(a),r(b),id(c){}
    bool operator < (const qry& rhs) const
    {
        return r<rhs.r;
    }
};

vector<qry>QRY;

void add(int pos,int val)
{
    while(pos<=N)
    {
        tree[pos]+=val;
        pos+=pos&(-pos);
    }
}

int sum(int pos)
{
    int ret=0;
    while(pos)
    {
        ret+=tree[pos];
        pos-=pos&(-pos);
    }
    return ret;
}

int main()
{
    while(scanf("%d %d",&N,&Q)!=EOF)
    {
        QRY.clear();
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=N;i++)
        {
            scanf("%d",&a[i]);
            vec[i].clear();
        }
        for(int i=1;i<=N;i++)
        {
            int x=a[i];
            int y=i;
            for(unsigned int j=0;j<vec[i-1].size();j++)
            {
                int GCD=__gcd(x,vec[i-1][j].first);
                if(x!=GCD)
                {
                    vec[i].push_back(make_pair(x,y));
                    x=GCD;y=vec[i-1][j].second;
                }
            }
            vec[i].push_back(make_pair(x,y));
        }
        int l,r;
        for(int i=1;i<=Q;i++)
        {
            scanf("%d %d",&l,&r);
            QRY.push_back(qry(l,r,i));
        }
        sort(QRY.begin(),QRY.end());
        memset(vis,0,sizeof(vis));
        int k=0;
        for(int i=1;i<=N;i++)
        {
            for(unsigned int j=0;j<vec[i].size();j++)
            {
                int x=vec[i][j].first;
                int y=vec[i][j].second;
                if(vis[x]) add(vis[x],-1);
                vis[x]=y;
                add(y,1);
            }
            while(QRY[k].r==i)
            {
                ANS[QRY[k].id]=sum(QRY[k].r)-sum(QRY[k].l-1);
                k++;
            }
        }
        for(int i=1;i<=Q;i++)
            printf("%d\n",ANS[i]);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值