hdu 4777 Rabbit Kingdom (离线树状数组)

题目大意:

给出m个查询,查询出[ l - r] 之间去 这个区间所有的数都互质的数有多少个。

思路分析:

首先我们处理出来每一个位置,左边和右边第一个与之不互质的数的位置。记在pre 和 next下。这个方法用分解质因数就好。

一个区间内的答案,等于这个区间的所有数减去有与之互质数的个数。

现在要统计的就是

1.对于一个给定的查询[l,r] 区间,统计有多少个 i (l<= i  <=r) 的pre[i] 或 next[i]在[l,r]内。

2.对于一个给定的查询[l,r]区间,统计有多个个i (l<= i < = r)的pre[i] 且 next[i]  在[l,r]内。

那么区间的答案就是  r-l+1-(统计出来的1的答案)+(统计出来的2的答案)。

对于统计2,我们就是判断有多个[pre[i],next[i] ] 在 [l,r]内。

对于统计1,我们可以用判断 [pre[i],i] 或 [i,next[i]]来替换。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define lowbit(x) (x&(-x))
#define maxn 200005
#define lson num<<1,s,mid
#define rson num<<1|1,mid+1,e

using namespace std;
bool vis[200005];
int prime[50000],top_prime;

int n,m;
struct foo
{
    int s,e;
    int index,ans;
    foo(){}
    foo(int st,int ed,int id):s(st),e(ed),index(id){}
    bool operator < (const foo &cmp)const
    {
        return e<cmp.e;
    }
}Q[200005];
bool cmp_id(foo a,foo b)
{
    return a.index<b.index;
}
int bit[maxn];
void update(int pos)
{
    if(pos==0 || pos==n+1)return ;
    for(int x=pos;x<=n;x+=lowbit(x))
        bit[x]++;
}
int query(int l,int r)
{
    int ans=0;
    for(int x=r;x>=1;x-=lowbit(x))ans+=bit[x];
    for(int x=l-1;x>=1;x-=lowbit(x))ans-=bit[x];
    return ans;
}
void sieve(int n)
{
    int m=(int)sqrt(n+0.5);
    memset(vis,0,sizeof(vis));
    for(int i=2;i<=m;i++)
    {
        if(!vis[i])
        {
            for(int j=i*i;j<=n;j+=i)
                vis[j]=1;
        }
    }
    for(int i=2;i<=m;i++)
    {
        if(vis[i]==0) prime[top_prime++]=i;
    }
}
int pre[200005],next[200005],now[200005],a[200005],tnext[200005];
vector<int> p[200005];

vector <foo> ret[3];
int ans[3][maxn];
void getans(int key)
{
    memset(bit,0,sizeof bit);

    int ind=0;
    for(int i=1;i<=m;i++)
    {
        while(ind<n&&ret[key][ind].e<=Q[i].e)
        {
            update(ret[key][ind].s);
            ind++;
        }
        ans[key][Q[i].index]+=query(Q[i].s,Q[i].e);
    }
}
int num[maxn];
int main()
{
    sieve(200000);
    while(~scanf("%d%d",&n,&m))
    {
        if(!n&&!m) break;
        memset(pre,0,sizeof pre);
        memset(next,0,sizeof next);
        memset(tnext,0,sizeof tnext);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            p[a[i]].clear();
        }
        for(int i=1;i<=n;i++)
        {
            if(p[a[i]].size()) continue;
            int tmp=a[i];
            for(int j=0;prime[j]*prime[j]<=tmp&&j<top_prime;j++)
            {
                if(tmp%prime[j]==0)
                {
                    p[a[i]].push_back(prime[j]);
                    while(tmp%prime[j]==0) tmp/=prime[j];
                }
            }
            if(tmp>1) p[a[i]].push_back(tmp);
        }
        memset(now,0,sizeof(now));
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<p[a[i]].size();j++)
            {
                int tmp=p[a[i]][j];
                pre[i]=max(now[tmp],pre[i]);
                now[tmp]=i;
            }
        }
        reverse(a+1,a+1+n);
        memset(now,0,sizeof(now));
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<p[a[i]].size();j++)
            {
                int tmp=p[a[i]][j];
                tnext[i]=max(now[tmp],tnext[i]);
                now[tmp]=i;
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(tnext[n-i+1]==0) next[i]=0;
            else next[i]=n-tnext[n-i+1]+1;
        }

        for(int i=1;i<=n;i++)
        {
            if(next[i]==0)next[i]=n+1;//next初始化为0
        }
        //以上为预处理出pre 和 next
        for(int i=1;i<=m;i++)
        {
            int st,ed;
            scanf("%d%d",&st,&ed);
            Q[i]=foo(st,ed,i);
            Q[i].ans=0;
            num[i]=ed-st+1;
        }
        for(int i=0;i<3;i++)ret[i].clear();

        for(int i=1;i<=n;i++)
        {
            ret[0].push_back(foo(pre[i],i,0));
            ret[1].push_back(foo(i,next[i],0));
            ret[2].push_back(foo(pre[i],next[i],0));
        }

        sort(Q+1,Q+1+m);
        for(int i=0;i<3;i++)
            sort(ret[i].begin(),ret[i].end());

        memset(ans,0,sizeof ans);
        for(int i=0;i<3;i++)
            getans(i);

        for(int i=1;i<=m;i++)
            printf("%d\n",num[i]-ans[0][i]-ans[1][i]+ans[2][i]);
    }
    return 0;
}
时间: 2024-10-12 16:46:35

hdu 4777 Rabbit Kingdom (离线树状数组)的相关文章

hdu 4777 Rabbit Kingdom(离线树状数组&amp;思维)

Rabbit Kingdom Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 964    Accepted Submission(s): 315 Problem Description Long long ago, there was an ancient rabbit kingdom in the forest. Every rab

HDU 4777 Rabbit Kingdom --容斥原理+树状数组

题意: 给一个数的序列,询问一些区间,问区间内与区间其他所有的数都互质的数有多少个. 解法: 直接搞有点难, 所谓正难则反,我们求区间内与其他随便某个数不互质的数有多少个,然后区间长度减去它就是答案了. 那么怎么求区间内与区间其他某个数不互质的数的个数(记为cnt)呢? 我们用L[i],R[i]表示在整个序列中左边与 i 最近的与 i 不互质的数的位置,R[i]表示右边的,L[i],R[i]我们可以正反扫一遍顺便分解因子,用个pos[]记录很方便地求出.那么区间内的cnt为L[i]或R[i]在区

HDU 4777 Rabbit Kingdom(树状数组)

HDU 4777 Rabbit Kingdom 题目链接 题意:给定一些序列,每次询问一个区间,求出这个区间和其他数字都互质的数的个数 #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int INF = 0x3f3f3f3f; typedef long long ll; const ll N = 200

13杭州区域赛现场赛Rabbit Kingdom(树状数组+离线)

题意:给你一个长度数列,再给你m个询问(一个区间),问你在这个区间里面有多少个数与其他的数都互质. 解题思路:你看这种类型的题目都可以肯定这是 离线+树状数组(线段树).主要就是他的更新信息.这里我的处理是先把1-200000(每个数的范围)数里面所有的质因子求出来.然后从后往前遍历数组.会出现以下几种情况 1.a[k]的质因子在后面出现过而[更新标记] 和[被更新标记] 都为假 2.a[k]的质因子在后面出现过的那个位置 I   [更新标记]为 真 . 3.a[k]的质因子在后面出现过且那个位

hdu 4417 Super Mario(离线树状数组|划分树)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2584    Accepted Submission(s): 1252 Problem Description Mario is world-famous plumber. His "burly" figure and amazing jumping a

hdu 4777 Rabbit Kingdom(树状数组)

题目链接:hdu 4777 Rabbit Kingdom 题目大意:一个兔子王国,有N只兔子,每只兔子有一个重量,如果两只兔子的重量不互质,那么就会干架,现在国王想将l r之间的兔子关进监狱,它想知道会有多少只兔子不会和别的兔子干架. 解题思路:预处理出每只兔子的L,R表示向左和向右最近会与该兔子发生冲突的兔子,预处理的时候只要将每只兔子的重量分解成质因子后遍历两遍. 对于询问,将询问按照右区间排序,碰到i,则L位置+1,碰到R,则i位置+1,L位置-1.(如果L ≤ l && r ≤ R

HDU 5156 - Harry and Christmas tree (dfs序+离线树状数组)

http://acm.hdu.edu.cn/showproblem.php?pid=5156 BC#25的C题. 题意是:给出一颗大小为n的树,以1为根,然后给出m次染色,每次将节点u加上一种颜色(一个节点可以有多个颜色). 最后查询树上每个节点对应子树上包含的不同颜色数量. 当时这场比赛没有做,回来看一下题目,没看标解就试着敲了一遍,于是解题思路从一开始就走上了不归路. 标解是O(n+m)的方法,主要思路是将问题转为:一次染色表示将u到根节点的路径都染上这种颜色. 但这样做需要去重,因为如果u

hdu 4605 Magic Ball Game (在线主席树/离线树状数组)

hdu 4605 题意: 有一颗树,根节点为1,每一个节点要么有两个子节点,要么没有,每个节点都有一个权值wi .然后,有一个球,附带值x . 球到达某个节点上,如果x==wi,那么球停在这个节点上 .当然,这个点是叶子节点也会停止 . 如果x<wi,那么有1/2的概率走向左子树,有1/2的概率走向右子树 . 如果x>wi,那么有1/8的概率走向左子树,有7/8的概率走向右子树 . 问球经过v节点的概率 .(停在v节点也算) 解法: 在线的话每一个节点建一棵根节点到该节点的线段树,离线的话就先

区间的关系的计数 HDU 4638 离线+树状数组

题目大意:给你n个人,每个人都有一个id,有m个询问,每次询问一个区间[l,r],问该区间内部有多少的id是连续的(单独的也算是一个) 思路:做了那么多离线+树状数组的题目,感觉这种东西就是一个模板了,23333,反正都是定义右区间的. 这题的关键难度就是如何定义id是连续的呢.我们每次往区间里面放一个数值以后都要add(pos, 1),就是把pos~n的所有的关系都+1.然后如果说在pos之前就出现id-1,就要add(pos[id-1], -1)(同理id+1也是这样),这样子表示从pos[

hdu 3015 Disharmony Trees (离散化+树状数组)

Disharmony Trees Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 663    Accepted Submission(s): 307 Problem Description One day Sophia finds a very big square. There are n trees in the square. T