CF359D Pair of Numbers

很容易发现满足题目要求的区间长度是符合二分性质的,即如果长度为len的[l, r]可行,那么一定至少存在一个长度为len-1的子区间[l+1, r]或[l, r-1]可行

 

所以就对长度进行二分答案

 

接下来的问题是如何判断一个区间是否符合要求

 

首先发现如果存在一个 aj 可以被 a[l,r] 中的所有元素整除,那么它一定是这个区间的最小值,并且它还是这个区间的所有元素的gcd

 

所以问题就转化成了区间查询最小值和gcd,如果一个区间的最小值和区间gcd相等,那么这个区间就符合条件

 

区间最小值比较好处理,那么区间gcd呢,其实求区间gcd就是在求区间内所有数对某个质因子的指数求最小(可以用算数基本原理模拟一下),本质上还是在求区间最小值

 

所以区间gcd和区间最小值一样具有可区间合并的性质,即min(min(l1, r1), min(l2, r2))=min(l1, r2)     gcd(gcd(l1, r1), gcd(l2, r2))=gcd(l1, r2)   (l1<=r1, l2<=r2)

 

由于只涉及到查询操作,所以可以用st表处理

 

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define db double
#define rep(x,a,b) for(int x=(a);x<=(b);x++)
#define per(x,a,b) for(int x=(a);x>=(b);x--)
#define reP(x,a,b) for(int x=(a);x<(b);x++)
#define Per(x,a,b) for(int x=(a);x>(b);x--)
#define scf(a) scanf("%d",&a)
#define scfll(a) scanf("%lld",&a)
#define scfdb(a) scanf("%lf",&a)
#define ptf(a) printf("%d",a)
#define ptfll(a) printf("%lld",a)
#define ptfsp(a) printf("%d ",a)
#define ptfllsp(a) printf("%lld ",a)
#define pli(a,b) make_pair(a,b)
#define pb push_back
#define el puts("")
#define FS first
#define SE second
#define PI acos(-1)
#define ls pos<<1
#define rs pos<<1|1
/*ios::sync_with_stdio(false);
freopen("in.txt","r",stdin);
freopen("1.out","w",stdout);*/
using namespace std;
const ll mod=998244353;
const int maxn=3e5+5;
int stgcd[maxn][20],stmin[maxn][20],lg2[maxn],n;
int getgcd(int l,int r){
    int j=lg2[r-l+1];
    return __gcd(stgcd[l][j],stgcd[r-(1<<j)+1][j]);
}
int getmin(int l,int r){
    int j=lg2[r-l+1];
    return min(stmin[l][j],stmin[r-(1<<j)+1][j]);
}
bool check(int mid){
    rep(i,1,n){
        int j=i+mid-1;
        if(j>n) break;
        if(getgcd(i,j)==getmin(i,j)) return 1;
    }
    return 0;
}
int ans[maxn],Ans,cnt;
int main(){
    scf(n);
    lg2[1]=0;
    rep(i,2,n) lg2[i]=lg2[i>>1]+1;
    rep(i,1,n){
        int x;scf(x);
        stgcd[i][0]=stmin[i][0]=x;
    }
    for(int j=1;j<=19;j++){
        rep(i,1,n){
            if(i+(1<<j-1)>n) break;
            stgcd[i][j]=__gcd(stgcd[i][j-1],stgcd[i+(1<<j-1)][j-1]);
            stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<j-1)][j-1]);
        }
    }
    int l=1,r=n,Ans=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid)) Ans=mid,l=mid+1;
        else r=mid-1;
    }
    rep(i,1,n){
        int j=i+Ans-1;
        if(j>n) break;
        if(getgcd(i,j)==getmin(i,j)) ans[++cnt]=i;
    }
    cout<<cnt<<" "<<Ans-1;el;
    rep(i,1,cnt) ptfsp(ans[i]);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值