gym 101492 C. Coprimes(bitset预处理 或 RMQ )*

题目链接: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;
}
参考:
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值