Gym - 101492C-Coprimes bitset和_Find_first的应用

Gym - 101492C

C - Coprimes

A. Tuttu (a distant relative of W. Tutte) is a young mathematician with a promising future. As a child, he was very lonely, since he had no siblings nor cousins. One of his earliest Christmas gifts was a Number Theory book. That is the reason he focused on studying this area since a very early age. He was very interested in coprimes, but he could not solve the following problem and he asked you to help him.

Given a sequence of N positive integers, we want to answer M queries. Each query is represented by two indices. He would like to know if there exists a pair of relatively prime numbers in the sequence whose positions are between the given indices.
Input
The first line has the numbers N and M separated by a space. The second line contains N positive integers a1, a2, ..., aN separated by a space. Then there are M lines, each one containing two integers,  and r, separated by a space, encoding a query.
2 ≤ N ≤ 5·104
1 ≤ M ≤ 2·105
1 ≤ ai ≤ 5·105, 1 ≤ i ≤ N
output
For each one of the queries you should print "S" (without the double quotes) if there is a pair of relatively prime integers between (including) the sequence positions indexed by  and r, or "N" otherwise.
Examples
5 3
6 15 10 6 7
1 3
2 4
4 5

N
N
S
题目大意:
给你N个数,M个询问,每个询问包含两个数L,R。问你是否在区间【L,R】存在互质的数。


思路:
M的范围超级大,最多只可以允许log查询,一想到log查询就自然而然地想到线段树。然后呢~~就不会怎么维护答案了。太菜了!xyr大佬讲了一下这题,才发现原来可以这样做:先预处理出每一位右边一对互质的数的右边那个数的位置。然后直接O(1)查询答案。至于怎么预处理详细解释在代码中。
#include<bits/stdc++.h>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<string.h>
#include<string>
#include<stdlib.h>
#include<bitset>
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
bitset<50010>pos[50010],now,mark;
int prime[500010];
int num[500010];
int a[50010];
int dp[50010];
int vis[500010];
int main()
{
    int cnt=0;
    //线性筛
    for(int i=0;i<500010;i++)
        vis[i]=1;
    vis[1]=vis[0]=0;
    for(int i=2;i<500010;i++)
    {
        if(vis[i]==1)
        {
            prime[cnt++]=i;
            for(int j=i+i;j<500010;j+=i)
                vis[j]=0;
        }
    }
    for(int i=0;i<cnt;i++)
        num[prime[i]]=i;//处理出每个质数的位置
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    dp[n+1]=50010;
    for(int i=n;i>=1;i--)
    {
        now.reset();//bitset把now重置为0
        mark[i]=1;//把当前位的mark置为1
        for(int j=0;prime[j]*prime[j]<=a[i];j++)
        {
            if(a[i]%prime[j]==0)
            {
                pos[num[prime[j]]][i]=1;
                now|=pos[num[prime[j]]];
                /*记录右边全部能够被这个质因数分解数字的位置
                或运算后为零的位置就是该数字右边与其互质的数*/
                while(a[i]%prime[j]==0)
                    a[i]/=prime[j];
            }
        }
        if(a[i]!=1)
        {
            pos[num[a[i]]][i]=1;
            now|=pos[num[a[i]]];
        }
        /*上面的质因数分解从O(n)优化到了根号级别
          因为大于sqrt(a[i])的质因数最多一个*/
        int k=(mark^now)._Find_first();
        /*先与mark抑或得到,此位为1的就是与它互质的数的位置
          然后_Find_first找到第一个1的位置,k表示右边第一个与它互质的数的位置*/
        dp[i]=min(dp[i+1],k);
        /*然后用dp[i]维护第i位上右边一个互质的两个数的右边数的位置*/
        /*注意不是右边与第i位的数互质的数的位置*/
        /*例如 12 35 7 5 
          dp[4]=50010
          dp[3]=4
          dp[2]=4
          dp[1]=2*/
    }
    while(m--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        if(dp[l]<=r)printf("S\n");
        else printf("N\n");
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值