题目链接:http://codeforces.com/gym/101492/problem/C
C. Coprimes
time limit per test3.0 s
memory limit per test512 MB
inputstandard input
outputstandard output
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
inputCopy
5 3
6 15 10 6 7
1 3
2 4
4 5
outputCopy
N
N
S
inputCopy
6 4
39 78 143 26 22 70
2 5
3 6
4 6
1 5
outputCopy
N
S
N
S
Note
Two numbers are called coprime (or relatively prime) if their greatest common divisor is the number 1.
bitset做法:
分析:
对于一个任意一个区间[ L,R],该做法就是利用bitset 预处理出以L为区间左端,然后求出满足区间内有互质的数对的最小右边界R,也就是程序中的dp[L]
bitset中有一个函数 _Find_first(),就是找出bitset中从低位往高位数(也就是从右往左数,bitset与数组不同的是,它的下标0在最右边 ,比如说bitset<2> b(“10”),cout<<b<<endl; 输出就是10,但是数字1是在b[1]这个位置,数字0是在b[0]这个位置)第一次出现1所在的位置,
[由于这个函数,因此遍历n个数的时候,从后往前遍历]
遍历的时候先利用已筛好的素数,求出它右边与它不互质的数,然后利用异或,得到与它互质的数
dp[i]表示的就是: 以第i个数为区间左端,dp[i]就是满足有互质对的最小区间右端
最后还要注意的就是开bitset数组pos时,很容易炸
二维bitset pos与a数组的大概关系:
具体看代码:
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+10;
int prime[N];
int num[N];
int a[N];
int dp[N];
bitset<50010> pos[42000],now,mark;
void init()
{
memset(prime,0,sizeof(prime));
for(int i=2;i<N;i++)
{
if(!prime[i])
{
prime[++prime[0]]=i;
num[i]=prime[0];
}
for(int j=1;j<=prime[0]&&i*prime[j]<N;j++)
{
prime[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
int main()
{
init();
int n,m;
int l, r;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
dp[n+1]=N;
for(int i=n;i>0;i--)
{
now.reset();//置零
mark[i]=1;
for(int j=1;j<=prime[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)//说明a[i]本身就是个素数,没有经过上面的a[i]%prime[j]里面的步骤
{
pos[num[a[i]]][i]=1;
now|=pos[num[a[i]]];
}
int k=(mark^now)._Find_first();//从右往左数第一个出现1(说明互质)的位置
dp[i]=min(dp[i+1],k);
}
for(int i=0;i<m;i++)
{
scanf("%d%d",&l,&r);
if(r>=dp[l]) cout<<"S"<<endl;
else cout<<"N"<<endl;
}
return 0;
}
RMQ做法:
在cf上看到有人是用RMQ做的,待写
alex20030190大佬的代码,看不懂,先放着:
#include <bits/stdc++.h>
#define FI(i,a,b) for(int i=(a);i<=(b);i++)
#define FD(i,a,b) for(int i=(a);i>=(b);i--)
#define LL long long
#define Ldouble long double
#define PI 3.14159265358979323846264338327950288419
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define mp make_pair
#define fi first
#define se second
using namespace std;
int n, reach[50005], q, l, r, s[50005];
int cur, u[500005];
int li[8], p;
void prepare(int X){//获得X的所有质因子
int temp = X;
p = 0;
for(int j = 2; j * j <= temp; j++){
if(temp % j == 0){
while(temp % j == 0) temp /= j;
li[++p] = j;
}
}
if(temp > 1) li[++p] = temp;
}
int f(){
int ret = 0;
FI(i, 0, (1 << p) - 1){
int prod = 1, mul = 1;
FI(j, 0, p - 1) if(i & (1 << j)){
mul *= -1;
prod *= li[j + 1];
}
ret += mul * u[prod];
}
return ret;
}
void upd(int d){
FI(i, 0, (1 << p) - 1){
int prod = 1;
FI(j, 0, p - 1) if(i & (1 << j)){
prod *= li[j + 1];
}
u[prod] += d;
}
}
void calc(){
int ptr = 0;
FI(i, 1, n){
while(cur == 0 && ptr < n){
ptr++;
//how many numbers coprime with it?
prepare(s[ptr]);
cur += f();
upd(1);
}
//gged
if(cur == 0){
FI(j, i, n) reach[j] = n + 1;
break;
}
reach[i] = ptr;
//remove this num
prepare(s[i]);
upd(-1);
cur -= f();
}
}
int main(){
scanf("%d %d", &n, &q);
FI(i, 1, n) scanf("%d", &s[i]);
calc();
// FI(i, 1, n) printf("%d%c", reach[i], i == n ? '\n':' ');
while(q--){
scanf("%d %d", &l, &r);
if(reach[l] <= r) printf("S\n");
else printf("N\n");
}
return 0;
}
letsbe大佬的代码,看不懂,先放着:
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5 ;
int n ,m ,l ,r ,arr[N] ,spf[N] ,cnt[N] ,to[N] ;
vector<int> factors ,dv[N] ,fact[N] ;
/*
int ///\\\
arr[i] 存储数据
spf[i] 存i的最小因子
cnt[i] 因子i出现的次数 》
to[i]
vector<int> ///\\\
i=0..n-1
fact[i] 表示第i个数的所有质因子
dv[i] 表示第i个数的所有因子
factors
*/
void init(){
int i=0;i<n;i++)
for(int go: dv[i])
cout << go << endl ;
}
void sieve()
{//存i的最小因子
for(int i=1;i<N;++i) spf[i]=i;
for(int i=2;i*i<N;++i)if(spf[i]==i){
for(int j=i*i;j<N;j+=i)if(spf[j]==j)spf[j]=i;
}
}
vector<int> factorize(int x)
{//获得x质因子分解
set<int> ret;
while(x!=1){
ret.insert(spf[x]);
x/=spf[x];
}
vector<int> vec;
for(int go:ret) vec.push_back(go);
return vec;
}
int inc_exc(int i,int idx=0,int d=1,int sign=-1){
if(idx==fact[i].size()){
if(d==1)return 0;
return sign*cnt[d] ;
}
return inc_exc(i,idx+1,d,sign) + inc_exc(i,idx+1,d*fact[i][idx],sign*-1) ;
}
int spars[17][N] ;
int LG[N] ;
void build(){
LG[0] = -1 ;
for(int i=0;i<n;++i){
LG[i+1] = LG[i] + !(i&(i+1)) ;
spars[0][i] = to[i] ;
}
for(int lg = 1 ;(1<<lg)<=n ;++lg){
for(int i =0;i+(1<<lg)<=n;++i){
int a = spars[lg-1][i] ;
int b = spars[lg-1][i+(1<<(lg-1))] ;
spars[lg][i] = min(a,b) ;
}
}
}
int RMQ(int s,int e){
if(s>e)swap(s,e) ;
int siz = e - s + 1 ;
int lg = LG[siz] ;
int a = spars[lg][s] ;
int b = spars[lg][e-(1<<lg)+1] ;
return min(a,b) ;
}
int main()
{
sieve();
for(int i=1;i<40;i++)
cout<<spf[i]<<endl;
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i){
to[i] = 1e9 ;
scanf("%d",arr+i);
fact[i] = factorize(arr[i]);
for(int j=1;j*j<=arr[i];++j) if(arr[i]%j==0){
dv[i].push_back(j);
if(j*j!=arr[i]) dv[i].push_back(arr[i]/j);
}
}
int i=0 ,j=1 ;
cout<<dv[0].size()<<endl;
for(int go:dv[0])
{
++cnt[go];
cout<<"go"<<go<<endl;
}
while(j<n){
int c = inc_exc(j);
if(c==j-i){
for(int go:dv[j]) ++cnt[go] ;
++j ;
}
else{
to[i] = j ;
for(int go:dv[i]) --cnt[go] ;
++i;
}
}
build();
while(m--){
scanf("%d%d",&l,&r);
--l ,--r ;
puts(RMQ(l,r)<=r?"S":"N");
}
return 0;
}