Gym - 101492C
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
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");
}
}