HDU-4777 Rabbit Kingdom

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=4777

题意:

每个兔子有一个数字,数字不互质的兔子会互相打架,现在给出一列兔子,有多组询问,问其中一个区间中的兔子有多少只是不会与其他兔子打架的。

思路:

13年区域赛的水题,,然而对于我来说并不水。

首先分析题意,明显就是求区间中与其他数字均互质的数字的数量。

给出的区间较多,可以考虑离线处理。

于是可以先求出每个数字在这列数中于所有数互质的最大左右区间长度,然后再一一判断询问区间是否包含每个数的最大区间。

这样就把题分成了两部分,预处理每个数的区间长度和离线处理每个询问,

预处理的部分好说,枚举每个数字的质因子,储存前一时刻当前质因子出现的位置,扫一边,前一时刻质因子出现的位置就可能是当前数字的左端点,取当前数字所有质因子的前一时刻位置的最小值就是当前数字的左端点,同时前一时刻这个质因子位置上的那个数的右端点就有可能是当前扫到的这个数,,这样可能有点乱,左右各扫一遍也行。

具体方法如下:

void hunt()
{
    for(int i=1;i<=n;i++)
        res[i].x=0,res[i].y=n+1,res[i].r=i;
    for(int i=1;i<N;i++)
        pre[i]=0;
    for(int i=1;i<=n;i++)
    {
        int now = num[i];
        for(int j=0;j<pri[now].size();j++)
        {
            int t=pri[now][j];
            res[i].x=max(res[i].x,pre[t]);
            res[pre[t]].y=min(res[pre[t]].y,i);
            pre[t] = i;
        }
    }
}

然后就是离线处理的部分,这个并没有想到,全赖网上的大神解题报告才明白要用树状数组。

只要对询问进行排序,可以以询问的右端点排序,之后判断所有数的左右区间端点。

如果res[i].r<=y则需要判断res[i].x是否小于x,所以把inverse[res[i].r]+=1;inverse[res[i].x]+=-1;之后取和时如果res[i].x在区间内则加起来是0,否则为1,

另外还有res[i].y<=y的情况,直接将inverse[res[i].r]+=-1;inverse[res[i].x]+=1;即可,因为这样包含它的右端点不可能全部互质了。

用树状数组实现很简单,然而并没有想到能这样取和求结果。

代码:

#define N 212345

int n,m;
int flag,sum,ave,len,ans1,ans2;
struct node
{
    int x,y;
    int r,w;
    friend bool operator < (node a, node b)
    {
        return a.y < b.y;
    }
}query[N],res[N],ress[N];
vector<int> pri[N];
int num[N],pre[N],inverse[N],ans[N];
void init()
{
    for(int i=0;i<N;i++)
        pri[i].clear();
    for(int i=2;i<N;i++)
        if(pri[i].size()==0)
            for(int j=i;j<N;j+=i)
                pri[j].push_back(i);
}
void hunt()
{
    for(int i=1;i<=n;i++)
        res[i].x=0,res[i].y=n+1,res[i].r=i;
    for(int i=1;i<N;i++)
        pre[i]=0;
    for(int i=1;i<=n;i++)
    {
        int now = num[i];
        for(int j=0;j<pri[now].size();j++)
        {
            int t=pri[now][j];
            res[i].x=max(res[i].x,pre[t]);
            res[pre[t]].y=min(res[pre[t]].y,i);
            pre[t] = i;
        }
    }
}
int lowbit(int t)
{
	return t & (t^(t-1));
}
void add(int pos,int num)
{
    if(pos==0) return ;
	while (pos<=n)
    {
		inverse[pos]+=num;
		pos+=lowbit(pos);
	}
}
int getSum(int now)
{
	int sum=0;
	while (now>0)
    {
		sum+=inverse[now];
		now-=lowbit(now);
	}
	return sum;
}
void solve()
{
    int i,j,k,x,y;
    for(i=1;i<=n;i++)
        ress[i]=res[i];
    memset(inverse,0,sizeof(inverse));
    sort(query+1,query+1+m);
    sort(res+1,res+1+n);
    j=1;k=1;
    for(i=1;i<=m;i++)
    {
        x = query[i].x; y = query[i].y;
        while(ress[j].r<=y && j<=n)
        {
            add(ress[j].r,1);
            add(ress[j].x,-1);
            j++;
        }
        while(res[k].y<=y && k<=n)
        {
            add(res[k].r,-1);
            add(res[k].x,1);
            k++;
        }
        ans[query[i].r] = getSum(y)-getSum(x-1);
    }
    for(i=1;i<=m;i++)
        printf("%d\n",ans[i]);
}
int main()
{
    int i,j,k,kk,t,x,y,z;
    init();
    while(scanf("%d%d",&n,&m)!=EOF&&n)
    {
        for(i=1;i<=n;i++)
            scanf("%d",&num[i]);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&query[i].x,&query[i].y);
            query[i].r = i;
        }
        hunt();
        solve();
    }
    return 0;
}












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值