CF359D Pair of Numbers [RMQ+ST算法]

题意:

给一串数,找出最长的区间使得这个区间里面有个数能被其他所有数整除(包括它自己),求满足这个条件的最长区间的个数及长度,以及这些区间的左端的位置

分析:

这个区间的要求其实就是GCD(ALL)=MIN(ALL),能被其他数整除,这个数肯定是最小的,然后又能被其他数整除(包括自己)这个数就是GCD了


可以二分枚举区间长度,然后验证答案的可靠性

对当前长度的所有区间,套用RMQ,验证是否存在一个区间的GCD=MIN

如果有这样的一个区间,那么说明当前长度可以,加大枚举的区间长度,否则减小

#include<iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include<cmath>
using namespace std;
#define MAXN 333333
int N;
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int num[MAXN];
int f1[MAXN][30];
int f2[MAXN][30];
void st(int n) {
	int i, j, k, m;
	k = (int) (log((double)n) / log(2.0));
	for(i = 0; i < n; i++) {
		f1[i][0] = num[i];
		f2[i][0] = num[i];
	}
	for(j = 1; j <= k; j++) {
		for(i = 0; i + (1 << j) - 1 < n; i++) {
			m = i + (1 << (j - 1));
			f1[i][j] = min(f1[i][j-1], f1[m][j-1]);
			f2[i][j] = gcd(f2[i][j-1], f2[m][j-1]);
		}
	}
}

//查询i和j之间的最值,注意i是从0开始的
int rmq_gcd(int i, int j) {
	int k = (int)(log(double(j-i+1)) / log(2.0)), t2;
	t2 = gcd(f2[i][k], f2[j - (1<<k) + 1][k]);
	return t2;
}
int rmq_min(int i, int j) {
	int k = (int)(log(double(j-i+1)) / log(2.0)), t1;
	t1 = min(f1[i][k], f1[j - (1<<k) + 1][k]);
	return t1;
}
bool check(int l){
        for(int i=0;i<N && i<=N-l;i++){
                if(rmq_gcd(i,i+l-1)==rmq_min(i,i+l-1))
                        return true;
        }
        return false;
}
vector<int>ans;
void get(int l){
        for(int i=0;i<N && i<=N-l;i++){
                if(rmq_gcd(i,i+l-1)==rmq_min(i,i+l-1))
                        ans.push_back(i+1);
        }
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("/home/rainto96/in.txt","r",stdin);
#endif

	int i;
	scanf("%d", &N);
	for (i = 0; i < N; i++) {
		scanf("%d", num+i);
	}
	st(N);
	int l=1,r=N,mid=(l+r)/2+1;
	while(l<r){
                if(check(mid))l=mid;
                else          r=mid-1;
                mid=(l+r)/2+1;
	}
	get(l);
	cout<<ans.size()<<' '<<l-1<<endl;
	for(int i=0;i<ans.size();i++){
                cout<<ans[i]<<' ';
	}
	cout<<endl;
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值