HDU 4777 Rabbit Kingdom(树状数组+离线预处理)

这是一个关于数学和算法的编程问题,要求解决在一个包含互质条件的兔子王国中,计算特定区间内兔子之间不会打架的数量。题目给出输入输出格式,并提供了一个参考解决方案,但作者表示对于如何使用树状数组进行维护和查询感到困惑。
摘要由CSDN通过智能技术生成

Problem Description
  Long long ago, there was an ancient rabbit kingdom in the forest. Every rabbit in this kingdom was not cute but totally pugnacious, so the kingdom was in chaos in season and out of season.
  n rabbits were numbered form 1 to n. All rabbits’ weight is an integer. For some unknown reason, two rabbits would fight each other if and only if their weight is NOT co-prime.
  Now the king had arranged the n rabbits in a line ordered by their numbers. The king planned to send some rabbits into prison. He wanted to know that, if he sent all rabbits between the i-th one and the j-th one(including the i-th one and the j-th one) into prison, how many rabbits in the prison would not fight with others.
  Please note that a rabbit would not fight with himself.

Input
  The input consists of several test cases.
  The first line of each test case contains two integer n, m, indicating the number of rabbits and the queries.
  The following line contains n integers, and the i-th integer Wi indicates the weight of the i-th rabbit.
  Then m lines follow. Each line represents a query. It contains two integers L and R, meaning the king wanted to ask about the situation that if he sent all rabbits from the L-th one to the R-th one into prison.
  (1 <= n, m, Wi <= 200000, 1 <= L <= R <= n)
  The input ends with n = 0 and m = 0.

Output
  For every query, output one line indicating the answer.

Sample Input
3 2
2 1 4
1 2
1 3
6 4
3 6 1 2 5 3
1 3
4 6
4 4
2 6
0 0

Sample Output
2
1
1
3
1
2
Hint
  In the second case, the answer of the 4-th query is 2, because only 1 and 5 is co-prime with other numbers in the interval [2,6] .

大致题意:给你一个长度为n的序列,有m次询问,每次查询一个区间,问这个区间里有多少个数和这个区间其他所有数互质

思路:参考dalao的博客http://blog.csdn.net/u011663071/article/details/39059483
弱鸡只能想到预处理出每个位置上的数字向左和向右最多能到达的位置,满足在这个区间内其他数与之互质,接下来怎么用树状数组去维护和查询就没思路了。。。。

代码如下

//#include<bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e5+10;
vector <int> Divisor[maxn]; //保存数i的所有约数 
vector <int> vc[maxn];//记录那些左边第一个非互质的位置出现在i的数的位置有哪些 
int Tree[maxn];
int v[maxn]; 
int vl[maxn];
int vr[maxn];
int f[maxn];//记录约数i第一次出现的位置 
int ans[maxn];//记录第i次查询的答案 
int n,m;
struct node
{
    int l,r,id;
}q[maxn];
bool cmp(node a,node b)
{
    return a.l<b.l;
}

int lowbit(int x)
{
    return x&-x;
}
int sum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=Tree[x];
        x-=lowbit(x);
    }
    return ans;
}
void add(int x,int date)
{
    while(x<=n)
    {
        Tree[x]+=date;
        x+=lowbit(x);
    }
    return ;
}

void pre()//预处理出每个数的左边第一个非互质的位置和右边第一个非互质的位置   
{
    for(int i=0;i<=n;i++)
        vc[i].clear();
    memset(f,0,sizeof(f));
    for (int i=1;i<=n;i++)  
        vl[i]=vr[i]=0; 
    for(int i=1;i<=n;i++)
    {
        for(int num=0;num<Divisor[v[i]].size();num++)
        {
            int p=Divisor[v[i]][num];
            if(f[p]==0)
            {
                f[p]=i;
                continue;
            }
            int wei=f[p];
            if(vr[wei]==0)
                vr[wei]=i;
            if(vl[i]<wei)
                vl[i]=wei;
            f[p]=i;
        }
        vc[vl[i]].push_back(i); 
    }
}
int main()
{
    for (int i=2;i<=200000;i++)  
        for (int t=i;t<=200000;t+=i)  
            Divisor[t].push_back(i); 

    while(scanf("%d%d",&n,&m)&&(n+m))
    {

        for(int i=1;i<=n;i++)
            scanf("%d",&v[i]);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;

        sort(q+1,q+n+1,cmp);//按查询区间左端点非递增排序 

        for (int i=1;i<=n;i++)  
            Tree[i]=0;   
        pre(); 
        int cnt=1;
        for(int i=0;i<=n;i++)
        {
            while(q[cnt].l==i)
            {
                ans[q[cnt].id]=sum(q[cnt].r)-sum(q[cnt].l-1);
                cnt++;    
            }    
            for(int num=0;num<vc[i].size();num++)
            {
                add(vc[i][num],1);
                if(vr[vc[i][num]]!=0)
                add(vr[vc[i][num]],-1);
            }
            if(vr[i]!=0)
            add(vr[i],1);
        } 
        for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值